diff options
Diffstat (limited to 'logic')
41 files changed, 575 insertions, 225 deletions
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index b92d50cc..cf86fda6 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -25,7 +25,7 @@ #include "logic/auth/MojangAccount.h" class QDialog; -class BaseUpdate; +class Task; class MinecraftProcess; class OneSixUpdate; class InstanceList; @@ -151,7 +151,7 @@ public: virtual SettingsObject &settings() const; /// returns a valid update task if update is needed, NULL otherwise - virtual BaseUpdate *doUpdate() = 0; + virtual Task *doUpdate() = 0; /// returns a valid minecraft process, ready for launch with the given account. virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0; diff --git a/logic/BaseUpdate.cpp b/logic/BaseUpdate.cpp deleted file mode 100644 index 5aeb12ef..00000000 --- a/logic/BaseUpdate.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "BaseUpdate.h" - -BaseUpdate::BaseUpdate(BaseInstance *inst, QObject *parent) : Task(parent) -{ - m_inst = inst; -} - -void BaseUpdate::updateDownloadProgress(qint64 current, qint64 total) -{ - emit progress(current, total); -}
\ No newline at end of file diff --git a/logic/BaseUpdate.h b/logic/BaseUpdate.h deleted file mode 100644 index ddeefa97..00000000 --- a/logic/BaseUpdate.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QObject> -#include <QList> -#include <QUrl> - -#include "net/NetJob.h" - -#include "tasks/Task.h" - -class MinecraftVersion; -class BaseInstance; - -/*! - * The game update task is the task that handles downloading instances' files. - */ -class BaseUpdate : public Task -{ - Q_OBJECT -public: - explicit BaseUpdate(BaseInstance *inst, QObject *parent = 0); - - virtual void executeTask() = 0; - -protected -slots: - // virtual void error(const QString &msg); - void updateDownloadProgress(qint64 current, qint64 total); - -protected: - BaseInstance *m_inst; -}; diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index c4df8220..0ef0f045 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -50,12 +50,9 @@ void InstanceLauncher::onLoginComplete() return; } console = new ConsoleWindow(proc); - console->show(); - - connect(proc, SIGNAL(ended()), SLOT(onTerminated())); - connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, - SLOT(write(QString, MessageLevel::Enum))); + connect(console, SIGNAL(isClosing()), this, SLOT(onTerminated())); + proc->setLogin(result.username, result.session_id); proc->launch(); */ } @@ -66,7 +63,7 @@ void InstanceLauncher::doLogin(const QString &errorMsg) loginDlg->exec(); if (loginDlg->result() == QDialog::Accepted) { - UserInfo uInfo{loginDlg->getUsername(), loginDlg->getPassword()}; + PasswordLogin uInfo{loginDlg->getUsername(), loginDlg->getPassword()}; ProgressDialog *tDialog = new ProgressDialog(nullptr); LoginTask *loginTask = new LoginTask(uInfo, tDialog); diff --git a/logic/JavaChecker.cpp b/logic/JavaChecker.cpp new file mode 100644 index 00000000..10b84fe1 --- /dev/null +++ b/logic/JavaChecker.cpp @@ -0,0 +1,89 @@ +#include "JavaChecker.h" +#include <QFile> +#include <QProcess> + +#define CHECKER_FILE "JavaChecker.jar" + +JavaChecker::JavaChecker(QObject *parent) : QObject(parent) +{ +} + +int JavaChecker::performCheck(QString path) +{ + if(QFile::exists(CHECKER_FILE)) + { + QFile::remove(CHECKER_FILE); + } + // extract the checker + QFile(":/java/checker.jar").copy(CHECKER_FILE); + + QStringList args = {"-jar", CHECKER_FILE}; + + process.reset(new QProcess()); + process->setArguments(args); + process->setProgram(path); + process->setProcessChannelMode(QProcess::SeparateChannels); + + connect(process.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(finished(int, QProcess::ExitStatus))); + connect(process.get(), SIGNAL(error(QProcess::ProcessError)), this, + SLOT(error(QProcess::ProcessError))); + connect(&killTimer, SIGNAL(timeout()), SLOT(timeout())); + killTimer.setSingleShot(true); + killTimer.start(5000); + process->start(); +} + +void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) +{ + killTimer.stop(); + QProcessPtr _process; + _process.swap(process); + + if (status == QProcess::CrashExit || exitcode == 1) + { + emit checkFinished({}); + return; + } + + QString p_stdout = _process->readAllStandardOutput(); + auto parts = p_stdout.split('=', QString::SkipEmptyParts); + if (parts.size() != 2 || parts[0] != "os.arch") + { + emit checkFinished({}); + return; + } + + auto os_arch = parts[1].remove('\n').remove('\r'); + bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; + + JavaCheckResult result; + { + result.valid = true; + result.is_64bit = is_64; + result.mojangPlatform = is_64 ? "64" : "32"; + result.realPlatform = os_arch; + } + emit checkFinished(result); +} + +void JavaChecker::error(QProcess::ProcessError err) +{ + if(err == QProcess::FailedToStart) + { + killTimer.stop(); + emit checkFinished({}); + return; + } +} + +void JavaChecker::timeout() +{ + // NO MERCY. NO ABUSE. + if(process) + { + process->kill(); + process.reset(); + emit checkFinished({}); + } +} diff --git a/logic/JavaChecker.h b/logic/JavaChecker.h new file mode 100644 index 00000000..60f8b56f --- /dev/null +++ b/logic/JavaChecker.h @@ -0,0 +1,32 @@ +#pragma once +#include <QProcess> +#include <QTimer> +#include <memory> + +struct JavaCheckResult +{ + QString mojangPlatform; + QString realPlatform; + bool valid = false; + bool is_64bit = false; +}; +typedef std::shared_ptr<QProcess> QProcessPtr; + +class JavaChecker : public QObject +{ + Q_OBJECT +public: + explicit JavaChecker(QObject *parent = 0); + int performCheck(QString path); + +signals: + void checkFinished(JavaCheckResult result); +private: + QProcessPtr process; + QTimer killTimer; +public +slots: + void timeout(); + void finished(int exitcode, QProcess::ExitStatus); + void error(QProcess::ProcessError); +}; diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h index 8d7550d0..44f576b4 100644 --- a/logic/JavaUtils.h +++ b/logic/JavaUtils.h @@ -33,7 +33,6 @@ public: QList<JavaVersionPtr> FindJavaPaths(); JavaVersionPtr GetDefaultJava(); - private: #if WINDOWS diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 3a337140..ab6536d0 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -44,7 +44,7 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, settings->registerSetting(new Setting("IntendedJarVersion", "")); } -BaseUpdate *LegacyInstance::doUpdate() +Task *LegacyInstance::doUpdate() { auto list = jarModList(); return new LegacyUpdate(this, this); @@ -59,7 +59,7 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); // extract the legacy launcher - QFile(":/launcher/launcher.jar").copy(PathCombine(minecraftRoot(), LAUNCHER_FILE)); + QFile(":/java/launcher.jar").copy(PathCombine(minecraftRoot(), LAUNCHER_FILE)); // set the process arguments { @@ -104,15 +104,15 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) args << "-jar" << LAUNCHER_FILE; args << account->currentProfile()->name(); - args << account->accessToken(); + args << account->sessionId(); args << windowTitle; args << windowSize; args << lwjgl; - proc->setMinecraftArguments(args); + proc->setArguments(args); } // set the process work path - proc->setMinecraftWorkdir(minecraftRoot()); + proc->setWorkdir(minecraftRoot()); return proc; } @@ -227,56 +227,18 @@ QString LegacyInstance::instanceConfigFolder() const return PathCombine(minecraftRoot(), "config"); } -/* -bool LegacyInstance::shouldUpdateCurrentVersion() const -{ - QFileInfo jar(runnableJar()); - return jar.lastModified().toUTC().toMSecsSinceEpoch() != lastCurrentVersionUpdate(); -} - -void LegacyInstance::updateCurrentVersion(bool keepCurrent) -{ - QFileInfo jar(runnableJar()); - - if(!jar.exists()) - { - setLastCurrentVersionUpdate(0); - setCurrentVersionId("Unknown"); - return; - } - - qint64 time = jar.lastModified().toUTC().toMSecsSinceEpoch(); - - setLastCurrentVersionUpdate(time); - if (!keepCurrent) - { - // TODO: Implement GetMinecraftJarVersion function. - QString newVersion = -"Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath()); - setCurrentVersionId(newVersion); - } -} -qint64 LegacyInstance::lastCurrentVersionUpdate() const -{ - I_D(LegacyInstance); - return d->m_settings->get ( "lastVersionUpdate" ).value<qint64>(); -} -void LegacyInstance::setLastCurrentVersionUpdate ( qint64 val ) -{ - I_D(LegacyInstance); - d->m_settings->set ( "lastVersionUpdate", val ); -} -*/ bool LegacyInstance::shouldRebuild() const { I_D(LegacyInstance); return d->m_settings->get("NeedsRebuild").toBool(); } + void LegacyInstance::setShouldRebuild(bool val) { I_D(LegacyInstance); d->m_settings->set("NeedsRebuild", val); } + QString LegacyInstance::currentVersionId() const { I_D(LegacyInstance); @@ -294,22 +256,26 @@ QString LegacyInstance::lwjglVersion() const I_D(LegacyInstance); return d->m_settings->get("LwjglVersion").toString(); } + void LegacyInstance::setLWJGLVersion(QString val) { I_D(LegacyInstance); d->m_settings->set("LwjglVersion", val); } + QString LegacyInstance::intendedVersionId() const { I_D(LegacyInstance); return d->m_settings->get("IntendedJarVersion").toString(); } + bool LegacyInstance::setIntendedVersionId(QString version) { settings().set("IntendedJarVersion", version); setShouldUpdate(true); return true; } + bool LegacyInstance::shouldUpdate() const { I_D(LegacyInstance); @@ -320,6 +286,7 @@ bool LegacyInstance::shouldUpdate() const } return true; } + void LegacyInstance::setShouldUpdate(bool val) { settings().set("ShouldUpdate", val); diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index e78bfd73..3d35521e 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -18,7 +18,7 @@ #include "BaseInstance.h" class ModList; -class BaseUpdate; +class Task; class LegacyInstance : public BaseInstance { @@ -78,7 +78,7 @@ public: virtual bool shouldUpdate() const; virtual void setShouldUpdate(bool val); - virtual BaseUpdate *doUpdate(); + virtual Task *doUpdate(); virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); virtual void cleanupAfterRun(); diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 8ba97827..05442917 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -26,7 +26,7 @@ #include <JlCompress.h> #include "logger/QsLog.h" -LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent) +LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst) { } diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h index b30fa0b3..8ffdb0f0 100644 --- a/logic/LegacyUpdate.h +++ b/logic/LegacyUpdate.h @@ -21,14 +21,13 @@ #include "logic/net/NetJob.h" #include "logic/tasks/Task.h" -#include "logic/BaseUpdate.h" class MinecraftVersion; class BaseInstance; class QuaZip; class Mod; -class LegacyUpdate : public BaseUpdate +class LegacyUpdate : public Task { Q_OBJECT public: @@ -72,4 +71,5 @@ private: private: NetJobPtr legacyDownloadJob; + BaseInstance *m_inst; }; diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index ff122628..e4a26054 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -59,12 +59,12 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst) connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut())); } -void MinecraftProcess::setMinecraftArguments(QStringList args) +void MinecraftProcess::setArguments(QStringList args) { m_args = args; } -void MinecraftProcess::setMinecraftWorkdir(QString path) +void MinecraftProcess::setWorkdir(QString path) { QDir mcDir(path); this->setWorkingDirectory(mcDir.absolutePath()); @@ -101,7 +101,7 @@ void MinecraftProcess::on_stdOut() for (int i = 0; i < lines.size() - 1; i++) { QString &line = lines[i]; - emit log(line /*.replace(username, "<Username>").replace(sessionID, "<Session ID>")*/, + emit log(line.replace(username, "<Username>").replace(sessionID, "<Session ID>"), getLevel(line, MessageLevel::Message)); } if (!complete) @@ -111,19 +111,24 @@ void MinecraftProcess::on_stdOut() // exit handler void MinecraftProcess::finish(int code, ExitStatus status) { - if (status != NormalExit) + if (!killed) { - // TODO: error handling + if (status == NormalExit) + { + //: Message displayed on instance exit + emit log(tr("Minecraft exited with exitcode %1.").arg(code)); + } + else + { + //: Message displayed on instance crashed + emit log(tr("Minecraft crashed with exitcode %1.").arg(code)); + } } - - // TODO: Localization - - if (!killed) - //: Message displayed on instance exit - emit log(tr("Minecraft exited with exitcode %1.").arg(status)); else + { //: 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)); @@ -134,11 +139,12 @@ void MinecraftProcess::finish(int code, ExitStatus status) m_prepostlaunchprocess.waitForFinished(); if (m_prepostlaunchprocess.exitStatus() != NormalExit) { - // TODO: error handling + emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), + m_prepostlaunchprocess.exitStatus()); } } m_instance->cleanupAfterRun(); - emit ended(m_instance); + emit ended(m_instance, code, status); } void MinecraftProcess::killMinecraft() @@ -155,7 +161,9 @@ void MinecraftProcess::launch() m_prepostlaunchprocess.waitForFinished(); if (m_prepostlaunchprocess.exitStatus() != NormalExit) { - // TODO: error handling + m_instance->cleanupAfterRun(); + emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(), + m_prepostlaunchprocess.exitStatus()); return; } } @@ -166,15 +174,15 @@ void MinecraftProcess::launch() 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("' '") /*.replace(username, "<Username>").replace(sessionID, "<Session -ID>")*/)); + m_args.join("' '").replace(username, "<Username>").replace(sessionID, "<Session ID>"))); start(JavaPath, m_args); if (!waitForStarted()) { //: Error message displayed if instace can't start - emit log(tr("Could not launch minecraft!")); + emit log(tr("Could not launch minecraft!"), MessageLevel::Error); + m_instance->cleanupAfterRun(); + emit launch_failed(m_instance); return; - // TODO: error handling } } diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index ad887c5b..e38d2f83 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -58,9 +58,14 @@ public: */ void launch(); - void setMinecraftWorkdir(QString path); + BaseInstance *instance() + { + return m_instance; + } + + void setWorkdir(QString path); - void setMinecraftArguments(QStringList args); + void setArguments(QStringList args); void killMinecraft(); @@ -72,9 +77,24 @@ public: signals: /** + * @brief emitted when Minecraft immediately fails to run + */ + void launch_failed(BaseInstance *); + + /** + * @brief emitted when the PreLaunchCommand fails + */ + void prelaunch_failed(BaseInstance *, int code, QProcess::ExitStatus status); + + /** + * @brief emitted when the PostLaunchCommand fails + */ + void postlaunch_failed(BaseInstance *, int code, QProcess::ExitStatus status); + + /** * @brief emitted when mc has finished and the PostLaunchCommand was run */ - void ended(BaseInstance *); + void ended(BaseInstance *, int code, QProcess::ExitStatus status); /** * @brief emitted when we want to log something diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index dbc42262..400aff2c 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -22,6 +22,8 @@ #include "net/S3ListBucket.h" #include "MultiMC.h" +#define ASSETS_URL "http://resources.download.minecraft.net/" + class ThreadedDeleter : public QThread { Q_OBJECT @@ -69,7 +71,7 @@ void OneSixAssets::downloadFinished() void OneSixAssets::S3BucketFinished() { - QString prefix("http://s3.amazonaws.com/Minecraft.Resources/"); + QString prefix(ASSETS_URL); nuke_whitelist.clear(); emit filesStarted(); @@ -114,7 +116,7 @@ void OneSixAssets::S3BucketFinished() void OneSixAssets::start() { auto job = new NetJob("Assets index"); - job->addNetAction(S3ListBucket::make(QUrl("http://s3.amazonaws.com/Minecraft.Resources/"))); + job->addNetAction(S3ListBucket::make(QUrl(ASSETS_URL))); connect(job, SIGNAL(succeeded()), SLOT(S3BucketFinished())); connect(job, SIGNAL(failed()), SIGNAL(failed())); emit indexStarted(); diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 5a83bafc..a947b7c0 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -18,6 +18,7 @@ #include "OneSixUpdate.h" #include "MinecraftProcess.h" #include "OneSixVersion.h" +#include "JavaChecker.h" #include <setting.h> #include <pathutils.h> @@ -36,7 +37,7 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o reloadFullVersion(); } -BaseUpdate *OneSixInstance::doUpdate() +Task *OneSixInstance::doUpdate() { return new OneSixUpdate(this); } @@ -74,7 +75,7 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) QMap<QString, QString> token_mapping; // yggdrasil! token_mapping["auth_username"] = account->username(); - //token_mapping["auth_session"] = response.session_id; + token_mapping["auth_session"] = account->sessionId(); token_mapping["auth_access_token"] = account->accessToken(); token_mapping["auth_player_name"] = account->currentProfile()->name(); token_mapping["auth_uuid"] = account->currentProfile()->id(); @@ -93,6 +94,8 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) token_mapping["game_directory"] = absRootDir; QString absAssetsDir = QDir("assets/").absolutePath(); token_mapping["game_assets"] = absAssetsDir; + //TODO: this is something new and not even fully implemented in the vanilla launcher. + token_mapping["user_properties"] = "{ }"; QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); for (int i = 0; i < parts.length(); i++) @@ -120,6 +123,11 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) 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) @@ -186,8 +194,8 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); - proc->setMinecraftArguments(args); - proc->setMinecraftWorkdir(minecraftRoot()); + proc->setArguments(args); + proc->setWorkdir(minecraftRoot()); return proc; } diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index c1c742a8..e30ca7ca 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -20,7 +20,7 @@ #include "BaseInstance.h" class OneSixVersion; -class BaseUpdate; +class Task; class ModList; class OneSixInstance : public BaseInstance @@ -39,8 +39,9 @@ public: QString loaderModsDir() const; virtual QString instanceConfigFolder() const; - virtual BaseUpdate *doUpdate(); + virtual Task *doUpdate(); virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); + virtual void cleanupAfterRun(); virtual QString intendedVersionId() const; diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h index 9cd1a226..5a13cbd9 100644 --- a/logic/OneSixRule.h +++ b/logic/OneSixRule.h @@ -16,7 +16,6 @@ #pragma once #include <QString> -#include <QSharedPointer> #include "logic/OneSixLibrary.h" diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 2d8a167c..c69ff155 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -28,10 +28,11 @@ #include "OneSixVersion.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" +#include "net/ForgeMirrors.h" #include "pathutils.h" -OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent) +OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst) { } @@ -142,7 +143,7 @@ void OneSixUpdate::jarlibStart() bool successful = inst->reloadFullVersion(); if (!successful) { - emitFailed("Failed to load the version description file (version.json). It might be " + emitFailed("Failed to load the version description file. It might be " "corrupted, missing or simply too new."); return; } @@ -163,20 +164,53 @@ void OneSixUpdate::jarlibStart() libs.append(version->getActiveNormalLibs()); auto metacache = MMC->metacache(); + QList<ForgeXzDownloadPtr> ForgeLibs; + bool already_forge_xz = false; for (auto lib : libs) { if (lib->hint() == "local") continue; - QString download_path = lib->downloadUrl(); - auto entry = metacache->resolveEntry("libraries", lib->storagePath()); + 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)); + + 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) { if (lib->hint() == "forge-pack-xz") - jarlibDownloadJob->addNetAction(ForgeXzDownload::make(download_path, entry)); + { + ForgeLibs.append(ForgeXzDownload::make(storage, entry)); + } else - jarlibDownloadJob->addNetAction(CacheDownload::make(download_path, entry)); + { + jarlibDownloadJob->addNetAction(CacheDownload::make(dl, entry)); + } } } + // TODO: think about how to propagate this from the original json file... or IF AT ALL + QString forgeMirrorList = "http://files.minecraftforge.net/mirror-brand.list"; + if (!ForgeLibs.empty()) + { + jarlibDownloadJob->addNetAction( + ForgeMirrors::make(ForgeLibs, jarlibDownloadJob, forgeMirrorList)); + } + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index e5f553c7..a66da067 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -21,12 +21,11 @@ #include "logic/net/NetJob.h" #include "logic/tasks/Task.h" -#include "logic/BaseUpdate.h" class MinecraftVersion; class BaseInstance; -class OneSixUpdate : public BaseUpdate +class OneSixUpdate : public Task { Q_OBJECT public: @@ -49,4 +48,5 @@ private: // target version, determined during this task std::shared_ptr<MinecraftVersion> targetVersion; + BaseInstance *m_inst; }; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 01bf41f4..4e2bbda5 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -151,7 +151,7 @@ std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root) root.value("minimumLauncherVersion").toDouble(); // ADD MORE HERE :D - if (launcher_ver > 0 && launcher_ver <= 9) + if (launcher_ver > 0 && launcher_ver <= 10) return fromJsonV4(root, readVersion); else { diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 4875e5f7..4f3839bc 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -82,6 +82,10 @@ void MojangAccount::setAccessToken(const QString& accessToken) m_accessToken = accessToken; } +QString MojangAccount::sessionId() const +{ + return "token:" + m_accessToken + ":" + currentProfile()->id(); +} const QList<AccountProfile> MojangAccount::profiles() const { diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index e5684b77..062b8aa2 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -110,13 +110,18 @@ public: * If the user has not chosen to stay logged in, this will be an empty string. */ QString accessToken() const; - + /** * Changes this MojangAccount's access token to the given value. */ void setAccessToken(const QString& token); /** + * Get full session ID + */ + QString sessionId() const; + + /** * Returns a list of the available account profiles. */ const ProfileList profiles() const; diff --git a/logic/lists/BaseVersionList.h b/logic/lists/BaseVersionList.h index 5ac9369b..21b44e8d 100644 --- a/logic/lists/BaseVersionList.h +++ b/logic/lists/BaseVersionList.h @@ -18,7 +18,6 @@ #include <QObject> #include <QVariant> #include <QAbstractListModel> -#include <QSharedPointer> #include "logic/BaseVersion.h" diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index 0d10e1f3..bf9e87b2 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -17,7 +17,6 @@ #include <QObject> #include <QAbstractListModel> -#include <QSharedPointer> #include <QUrl> #include <QNetworkReply> diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index c3e3561d..d08501eb 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -16,7 +16,6 @@ #pragma once #include <QObject> -#include <QSharedPointer> #include <QAbstractListModel> #include "categorizedsortfilterproxymodel.h" #include <QIcon> diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h index 4826ca0c..f816c932 100644 --- a/logic/lists/JavaVersionList.h +++ b/logic/lists/JavaVersionList.h @@ -17,7 +17,6 @@ #include <QObject> #include <QAbstractListModel> -#include <QSharedPointer> #include "BaseVersionList.h" #include "logic/tasks/Task.h" diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h index 90b1ae86..82af1009 100644 --- a/logic/lists/MinecraftVersionList.h +++ b/logic/lists/MinecraftVersionList.h @@ -18,7 +18,6 @@ #include <QObject> #include <QList> #include <QSet> -#include <QSharedPointer> #include "BaseVersionList.h" #include "logic/tasks/Task.h" diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 4fe4e68e..873d3a2e 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -29,7 +29,6 @@ CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry) m_entry = entry; m_target_path = entry->getFullPath(); m_status = Job_NotStarted; - m_opened_for_saving = false; } void CacheDownload::start() @@ -87,7 +86,7 @@ void CacheDownload::downloadFinished() // nothing went wrong... m_status = Job_Finished; - if (m_opened_for_saving) + if (m_output_file.isOpen()) { // save the data to the downloadable if we aren't saving to file m_output_file.close(); @@ -133,7 +132,7 @@ void CacheDownload::downloadFinished() void CacheDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_output_file.isOpen()) { if (!m_output_file.open(QIODevice::WriteOnly)) { @@ -144,7 +143,6 @@ void CacheDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } QByteArray ba = m_reply->readAll(); md5sum.addData(ba); diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h index 2b9a5dd8..e25aecd2 100644 --- a/logic/net/CacheDownload.h +++ b/logic/net/CacheDownload.h @@ -26,8 +26,6 @@ class CacheDownload : public NetAction Q_OBJECT public: MetaEntryPtr m_entry; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp index 6b2aa66f..239af351 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/FileDownload.cpp @@ -25,7 +25,6 @@ FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() m_target_path = target_path; m_check_md5 = false; m_status = Job_NotStarted; - m_opened_for_saving = false; } void FileDownload::start() @@ -113,7 +112,7 @@ void FileDownload::downloadFinished() void FileDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_output_file.isOpen()) { if (!m_output_file.open(QIODevice::WriteOnly)) { @@ -124,7 +123,6 @@ void FileDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } m_output_file.write(m_reply->readAll()); } diff --git a/logic/net/FileDownload.h b/logic/net/FileDownload.h index 31e0259c..58380e86 100644 --- a/logic/net/FileDownload.h +++ b/logic/net/FileDownload.h @@ -29,8 +29,6 @@ public: bool m_check_md5; /// the expected md5 checksum QString m_expected_md5; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any diff --git a/logic/net/ForgeMirror.h b/logic/net/ForgeMirror.h new file mode 100644 index 00000000..2518dffe --- /dev/null +++ b/logic/net/ForgeMirror.h @@ -0,0 +1,10 @@ +#pragma once +#include <QString> + +struct ForgeMirror +{ + QString name; + QString logo_url; + QString website_url; + QString mirror_url; +};
\ No newline at end of file diff --git a/logic/net/ForgeMirrors.cpp b/logic/net/ForgeMirrors.cpp new file mode 100644 index 00000000..fd7eccca --- /dev/null +++ b/logic/net/ForgeMirrors.cpp @@ -0,0 +1,116 @@ +#include "MultiMC.h" +#include "ForgeMirrors.h" +#include "logger/QsLog.h" +#include <algorithm> +#include <random> + +ForgeMirrors::ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) +{ + m_libs = libs; + m_parent_job = parent_job; + m_url = QUrl(mirrorlist); + m_status = Job_NotStarted; +} + +void ForgeMirrors::start() +{ + QLOG_INFO() << "Downloading " << m_url.toString(); + QNetworkRequest request(m_url); + request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); + auto worker = MMC->qnam(); + QNetworkReply *rep = worker->get(request); + + m_reply = std::shared_ptr<QNetworkReply>(rep); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress(qint64, qint64))); + connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); +} + +void ForgeMirrors::downloadError(QNetworkReply::NetworkError error) +{ + // error happened during download. + QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit() + << "Network error: " << error; + m_status = Job_Failed; +} + +void ForgeMirrors::downloadFinished() +{ + // if the download succeeded + if (m_status != Job_Failed) + { + // nothing went wrong... ? + parseMirrorList(); + return; + } + // else the download failed, we use a fixed list + else + { + m_status = Job_Finished; + m_reply.reset(); + deferToFixedList(); + return; + } +} + +void ForgeMirrors::deferToFixedList() +{ + m_mirrors.clear(); + m_mirrors.append( + {"Minecraft Forge", "http://files.minecraftforge.net/forge_logo.png", + "http://files.minecraftforge.net/", "http://files.minecraftforge.net/maven/"}); + m_mirrors.append({"Creeper Host", + "http://files.minecraftforge.net/forge_logo.png", + "https://www.creeperhost.net/link.php?id=1", + "http://new.creeperrepo.net/forge/maven/"}); + injectDownloads(); + emit succeeded(index_within_job); +} + +void ForgeMirrors::parseMirrorList() +{ + m_status = Job_Finished; + auto data = m_reply->readAll(); + m_reply.reset(); + auto dataLines = data.split('\n'); + for(auto line: dataLines) + { + auto elements = line.split('!'); + if (elements.size() == 4) + { + m_mirrors.append({elements[0],elements[1],elements[2],elements[3]}); + } + } + if(!m_mirrors.size()) + deferToFixedList(); + injectDownloads(); + emit succeeded(index_within_job); +} + +void ForgeMirrors::injectDownloads() +{ + // shuffle the mirrors randomly + std::random_device rd; + std::mt19937 rng(rd()); + std::shuffle(m_mirrors.begin(), m_mirrors.end(), rng); + + // tell parent to download the libs + for(auto lib: m_libs) + { + lib->setMirrors(m_mirrors); + m_parent_job->addNetAction(lib); + } +} + +void ForgeMirrors::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + emit progress(index_within_job, bytesReceived, bytesTotal); +} + +void ForgeMirrors::downloadReadyRead() +{ +} diff --git a/logic/net/ForgeMirrors.h b/logic/net/ForgeMirrors.h new file mode 100644 index 00000000..990e49d6 --- /dev/null +++ b/logic/net/ForgeMirrors.h @@ -0,0 +1,58 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "NetAction.h" +#include "HttpMetaCache.h" +#include "ForgeXzDownload.h" +#include "NetJob.h" +#include <QFile> +#include <QTemporaryFile> +typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr; + +class ForgeMirrors : public NetAction +{ + Q_OBJECT +public: + QList<ForgeXzDownloadPtr> m_libs; + NetJobPtr m_parent_job; + QList<ForgeMirror> m_mirrors; + +public: + explicit ForgeMirrors(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist); + static ForgeMirrorsPtr make(QList<ForgeXzDownloadPtr> &libs, NetJobPtr parent_job, + QString mirrorlist) + { + return ForgeMirrorsPtr(new ForgeMirrors(libs, parent_job, mirrorlist)); + } + +protected +slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +private: + void parseMirrorList(); + void deferToFixedList(); + void injectDownloads(); + +public +slots: + virtual void start(); +}; diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 6c9d7a60..f119878a 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -22,30 +22,45 @@ #include <QDateTime> #include "logger/QsLog.h" -ForgeXzDownload::ForgeXzDownload(QUrl url, MetaEntryPtr entry) : NetAction() +ForgeXzDownload::ForgeXzDownload(QString relative_path, MetaEntryPtr entry) : NetAction() { - QString urlstr = url.toString(); - urlstr.append(".pack.xz"); - m_url = QUrl(urlstr); m_entry = entry; m_target_path = entry->getFullPath(); + m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX"); m_status = Job_NotStarted; - m_opened_for_saving = false; + m_url_path = relative_path; +} + +void ForgeXzDownload::setMirrors(QList<ForgeMirror> &mirrors) +{ + m_mirror_index = 0; + m_mirrors = mirrors; + updateUrl(); } void ForgeXzDownload::start() { + m_status = Job_InProgress; if (!m_entry->stale) { + m_status = Job_Finished; emit succeeded(index_within_job); return; } // can we actually create the real, final file? if (!ensureFilePathExists(m_target_path)) { + m_status = Job_Failed; + emit failed(index_within_job); + return; + } + if (m_mirrors.empty()) + { + m_status = Job_Failed; emit failed(index_within_job); return; } + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); @@ -75,14 +90,53 @@ void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) m_status = Job_Failed; } +void ForgeXzDownload::failAndTryNextMirror() +{ + m_status = Job_Failed; + int next = m_mirror_index + 1; + if(m_mirrors.size() == next) + m_mirror_index = 0; + else + m_mirror_index = next; + + updateUrl(); + emit failed(index_within_job); +} + +void ForgeXzDownload::updateUrl() +{ + QLOG_INFO() << "Updating URL for " << m_url_path; + for (auto possible : m_mirrors) + { + QLOG_INFO() << "Possible: " << possible.name << " : " << possible.mirror_url; + } + QString aggregate = m_mirrors[m_mirror_index].mirror_url + m_url_path + ".pack.xz"; + m_url = QUrl(aggregate); +} + void ForgeXzDownload::downloadFinished() { + //TEST: defer to other possible mirrors (autofail the first one) + /* + QLOG_INFO() <<"dl " << index_within_job << " mirror " << m_mirror_index; + if( m_mirror_index == 0) + { + QLOG_INFO() <<"dl " << index_within_job << " AUTOFAIL"; + m_status = Job_Failed; + m_pack200_xz_file.close(); + m_pack200_xz_file.remove(); + m_reply.reset(); + failAndTryNextMirror(); + return; + } + */ + // if the download succeeded if (m_status != Job_Failed) { // nothing went wrong... m_status = Job_Finished; - if (m_opened_for_saving) + if (m_pack200_xz_file.isOpen()) { // we actually downloaded something! process and isntall it decompressAndInstall(); @@ -90,7 +144,8 @@ void ForgeXzDownload::downloadFinished() } else { - // something bad happened + // something bad happened -- on the local machine! + m_status = Job_Failed; m_pack200_xz_file.remove(); m_reply.reset(); emit failed(index_within_job); @@ -100,10 +155,11 @@ void ForgeXzDownload::downloadFinished() // else the download failed else { + m_status = Job_Failed; m_pack200_xz_file.close(); m_pack200_xz_file.remove(); m_reply.reset(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } } @@ -111,7 +167,7 @@ void ForgeXzDownload::downloadFinished() void ForgeXzDownload::downloadReadyRead() { - if (!m_opened_for_saving) + if (!m_pack200_xz_file.isOpen()) { if (!m_pack200_xz_file.open()) { @@ -122,7 +178,6 @@ void ForgeXzDownload::downloadReadyRead() emit failed(index_within_job); return; } - m_opened_for_saving = true; } m_pack200_xz_file.write(m_reply->readAll()); } @@ -138,7 +193,7 @@ void ForgeXzDownload::decompressAndInstall() // rewind the downloaded temp file m_pack200_xz_file.seek(0); // de-xz'd file - QTemporaryFile pack200_file; + QTemporaryFile pack200_file("./dl_temp.XXXXXX"); pack200_file.open(); bool xz_success = false; @@ -155,7 +210,7 @@ void ForgeXzDownload::decompressAndInstall() if (s == nullptr) { xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } b.in = in; @@ -180,7 +235,7 @@ void ForgeXzDownload::decompressAndInstall() { // msg = "Write error\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } @@ -214,42 +269,43 @@ void ForgeXzDownload::decompressAndInstall() case XZ_MEM_ERROR: QLOG_ERROR() << "Memory allocation failed\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_MEMLIMIT_ERROR: QLOG_ERROR() << "Memory usage limit reached\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_FORMAT_ERROR: QLOG_ERROR() << "Not a .xz file\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_OPTIONS_ERROR: QLOG_ERROR() << "Unsupported options in the .xz headers\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; case XZ_DATA_ERROR: case XZ_BUF_ERROR: QLOG_ERROR() << "File is corrupt\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; default: QLOG_ERROR() << "Bug!\n"; xz_dec_end(s); - emit failed(index_within_job); + failAndTryNextMirror(); return; } } } + m_pack200_xz_file.remove(); // revert pack200 pack200_file.close(); @@ -260,20 +316,22 @@ void ForgeXzDownload::decompressAndInstall() } catch (std::runtime_error &err) { + m_status = Job_Failed; QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); QFile f(m_target_path); if (f.exists()) f.remove(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } + pack200_file.remove(); QFile jar_file(m_target_path); if (!jar_file.open(QIODevice::ReadOnly)) { jar_file.remove(); - emit failed(index_within_job); + failAndTryNextMirror(); return; } m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5) diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h index 9f1bb029..990f91f0 100644 --- a/logic/net/ForgeXzDownload.h +++ b/logic/net/ForgeXzDownload.h @@ -19,6 +19,8 @@ #include "HttpMetaCache.h" #include <QFile> #include <QTemporaryFile> +#include "ForgeMirror.h" + typedef std::shared_ptr<class ForgeXzDownload> ForgeXzDownloadPtr; class ForgeXzDownload : public NetAction @@ -26,19 +28,24 @@ class ForgeXzDownload : public NetAction Q_OBJECT public: MetaEntryPtr m_entry; - /// is the saving file already open? - bool m_opened_for_saving; /// if saving to file, use the one specified in this string QString m_target_path; /// this is the output file, if any QTemporaryFile m_pack200_xz_file; + /// mirror index (NOT OPTICS, I SWEAR) + int m_mirror_index = 0; + /// list of mirrors to use. Mirror has the url base + QList<ForgeMirror> m_mirrors; + /// path relative to the mirror base + QString m_url_path; public: - explicit ForgeXzDownload(QUrl url, MetaEntryPtr entry); - static ForgeXzDownloadPtr make(QUrl url, MetaEntryPtr entry) + explicit ForgeXzDownload(QString relative_path, MetaEntryPtr entry); + static ForgeXzDownloadPtr make(QString relative_path, MetaEntryPtr entry) { - return ForgeXzDownloadPtr(new ForgeXzDownload(url, entry)); + return ForgeXzDownloadPtr(new ForgeXzDownload(relative_path, entry)); } + void setMirrors(QList<ForgeMirror> & mirrors); protected slots: @@ -53,4 +60,6 @@ slots: private: void decompressAndInstall(); + void failAndTryNextMirror(); + void updateUrl(); }; diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index e91d2684..08b39fe2 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -15,7 +15,6 @@ #pragma once #include <QString> -#include <QSharedPointer> #include <QMap> #include <qtimer.h> diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 4a789bb4..5607447e 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -27,7 +27,8 @@ #include <QJsonParseError> #include <QJsonObject> -LoginTask::LoginTask(const UserInfo &uInfo, QObject *parent) : Task(parent), uInfo(uInfo) +LoginTask::LoginTask(const PasswordLogin &loginInfo, QObject *parent) + : Task(parent), loginInfo(loginInfo) { } @@ -49,8 +50,8 @@ void LoginTask::legacyLogin() "application/x-www-form-urlencoded"); QUrlQuery params; - params.addQueryItem("user", uInfo.username); - params.addQueryItem("password", uInfo.password); + params.addQueryItem("user", loginInfo.username); + params.addQueryItem("password", loginInfo.password); params.addQueryItem("version", "13"); netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); @@ -221,8 +222,8 @@ void LoginTask::yggdrasilLogin() agent.insert("name", QString("Minecraft")); agent.insert("version", QJsonValue(1)); root.insert("agent", agent); - root.insert("username", uInfo.username); - root.insert("password", uInfo.password); + root.insert("username", loginInfo.username); + root.insert("password", loginInfo.password); root.insert("clientToken", clientToken); QJsonDocument requestDoc(root); netReply = worker->post(netRequest, requestDoc.toJson()); @@ -247,6 +248,7 @@ void LoginTask::yggdrasilLogin() void LoginTask::parseYggdrasilReply(QByteArray data) { QJsonParseError jsonError; + QLOG_DEBUG() << data; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); if (jsonError.error != QJsonParseError::NoError) { @@ -273,6 +275,7 @@ void LoginTask::parseYggdrasilReply(QByteArray data) playerID = selectedProfileO.value("id").toString(); playerName = selectedProfileO.value("name").toString(); } + QString sessionID = "token:" + accessToken + ":" + playerID; /* struct LoginResponse @@ -285,6 +288,6 @@ void LoginTask::parseYggdrasilReply(QByteArray data) }; */ - result = {uInfo.username, sessionID, playerName, playerID, accessToken}; + result = {loginInfo.username, sessionID, playerName, playerID, accessToken}; emitSucceeded(); } diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h index 26ac0808..fe4e6d2f 100644 --- a/logic/net/LoginTask.h +++ b/logic/net/LoginTask.h @@ -16,14 +16,20 @@ #pragma once #include "logic/tasks/Task.h" -#include <QSharedPointer> +#include <QMap> -struct UserInfo +struct PasswordLogin { QString username; QString password; }; +struct User +{ + QString id; + QMap<QString, QString> properties; +}; + struct LoginResponse { QString username; @@ -31,6 +37,7 @@ struct LoginResponse QString player_name; QString player_id; QString access_token; + User user; // FIXME: no idea what this really is yet. anything relevant? }; class QNetworkReply; @@ -39,7 +46,7 @@ class LoginTask : public Task { Q_OBJECT public: - explicit LoginTask(const UserInfo &uInfo, QObject *parent = 0); + explicit LoginTask(const PasswordLogin &loginInfo, QObject *parent = 0); LoginResponse getResult() { return result; @@ -65,5 +72,5 @@ protected: LoginResponse result; QNetworkReply *netReply; - UserInfo uInfo; + PasswordLogin loginInfo; }; diff --git a/logic/net/NetJob.cpp b/logic/net/NetJob.cpp index 21c6d3f7..333cdcbf 100644 --- a/logic/net/NetJob.cpp +++ b/logic/net/NetJob.cpp @@ -89,6 +89,7 @@ void NetJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) void NetJob::start() { QLOG_INFO() << m_job_name.toLocal8Bit() << " started."; + m_running = true; for (auto iter : downloads) { connect(iter.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h index c5c0d00c..021a1550 100644 --- a/logic/net/NetJob.h +++ b/logic/net/NetJob.h @@ -40,6 +40,16 @@ public: downloads.append(action); parts_progress.append(part_info()); total_progress++; + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + emit progress(current_progress, total_progress); + connect(base.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(base.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(base.get(), SIGNAL(progress(int, qint64, qint64)), + SLOT(partProgress(int, qint64, qint64))); + base->start(); + } return true; } |