summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
Diffstat (limited to 'logic')
-rw-r--r--logic/BaseInstance.h4
-rw-r--r--logic/BaseUpdate.cpp26
-rw-r--r--logic/BaseUpdate.h47
-rw-r--r--logic/InstanceLauncher.cpp9
-rw-r--r--logic/JavaChecker.cpp89
-rw-r--r--logic/JavaChecker.h32
-rw-r--r--logic/JavaUtils.h1
-rw-r--r--logic/LegacyInstance.cpp57
-rw-r--r--logic/LegacyInstance.h4
-rw-r--r--logic/LegacyUpdate.cpp2
-rw-r--r--logic/LegacyUpdate.h4
-rw-r--r--logic/MinecraftProcess.cpp44
-rw-r--r--logic/MinecraftProcess.h26
-rw-r--r--logic/OneSixAssets.cpp6
-rw-r--r--logic/OneSixInstance.cpp16
-rw-r--r--logic/OneSixInstance.h5
-rw-r--r--logic/OneSixRule.h1
-rw-r--r--logic/OneSixUpdate.cpp46
-rw-r--r--logic/OneSixUpdate.h4
-rw-r--r--logic/OneSixVersion.cpp2
-rw-r--r--logic/auth/MojangAccount.cpp4
-rw-r--r--logic/auth/MojangAccount.h7
-rw-r--r--logic/lists/BaseVersionList.h1
-rw-r--r--logic/lists/ForgeVersionList.h1
-rw-r--r--logic/lists/InstanceList.h1
-rw-r--r--logic/lists/JavaVersionList.h1
-rw-r--r--logic/lists/MinecraftVersionList.h1
-rw-r--r--logic/net/CacheDownload.cpp6
-rw-r--r--logic/net/CacheDownload.h2
-rw-r--r--logic/net/FileDownload.cpp4
-rw-r--r--logic/net/FileDownload.h2
-rw-r--r--logic/net/ForgeMirror.h10
-rw-r--r--logic/net/ForgeMirrors.cpp116
-rw-r--r--logic/net/ForgeMirrors.h58
-rw-r--r--logic/net/ForgeXzDownload.cpp100
-rw-r--r--logic/net/ForgeXzDownload.h19
-rw-r--r--logic/net/HttpMetaCache.h1
-rw-r--r--logic/net/LoginTask.cpp15
-rw-r--r--logic/net/LoginTask.h15
-rw-r--r--logic/net/NetJob.cpp1
-rw-r--r--logic/net/NetJob.h10
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;
}