summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
authorJan Dalheimer <jan@dalheimer.de>2014-03-10 19:24:29 +0100
committerJan Dalheimer <jan@dalheimer.de>2014-03-10 19:24:29 +0100
commitfcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb (patch)
tree851d8f8b6e6734e26fd2e4dc7b7477630329ff01 /logic
parent73fc9c79cff979e9023df0b1a77848c67b590681 (diff)
parentd11f10ea1ed54336254838ff068258d2d42e0774 (diff)
downloadMultiMC-fcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb.tar
MultiMC-fcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb.tar.gz
MultiMC-fcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb.tar.lz
MultiMC-fcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb.tar.xz
MultiMC-fcc5bc2ce0a1c8c3f9df9230710dd60363eb5cdb.zip
Merge branch 'develop' into feature_badges
Conflicts: logic/OneSixInstance.cpp
Diffstat (limited to 'logic')
-rw-r--r--logic/BaseInstaller.cpp2
-rw-r--r--logic/BaseInstance.cpp5
-rw-r--r--logic/BaseInstance.h2
-rw-r--r--logic/ForgeInstaller.cpp6
-rw-r--r--logic/ForgeInstaller.h4
-rw-r--r--logic/LiteLoaderInstaller.cpp4
-rw-r--r--logic/MMCJson.cpp61
-rw-r--r--logic/MMCJson.h46
-rw-r--r--logic/MinecraftProcess.cpp199
-rw-r--r--logic/MinecraftProcess.h7
-rw-r--r--logic/OneSixFTBInstance.cpp81
-rw-r--r--logic/OneSixInstance.cpp59
-rw-r--r--logic/OneSixInstance.h18
-rw-r--r--logic/OneSixInstance_p.h6
-rw-r--r--logic/OneSixLibrary.h3
-rw-r--r--logic/OneSixUpdate.cpp48
-rw-r--r--logic/OneSixVersion.cpp221
-rw-r--r--logic/OneSixVersionBuilder.cpp1111
-rw-r--r--logic/OneSixVersionBuilder.h31
-rw-r--r--logic/VersionFile.cpp535
-rw-r--r--logic/VersionFile.h127
-rw-r--r--logic/VersionFinal.cpp183
-rw-r--r--logic/VersionFinal.h (renamed from logic/OneSixVersion.h)23
-rw-r--r--logic/lists/LiteLoaderVersionList.cpp55
-rw-r--r--logic/lists/LiteLoaderVersionList.h11
-rw-r--r--logic/net/PasteUpload.cpp11
-rw-r--r--logic/net/PasteUpload.h2
27 files changed, 1420 insertions, 1441 deletions
diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp
index 92aa0c92..669fd0ac 100644
--- a/logic/BaseInstaller.cpp
+++ b/logic/BaseInstaller.cpp
@@ -17,7 +17,7 @@
#include <QFile>
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index 24af20cf..c7b29548 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -168,6 +168,11 @@ bool BaseInstance::canLaunch() const
return !flags().contains(VersionBrokenFlag);
}
+bool BaseInstance::reload()
+{
+ return settings().reload();
+}
+
QString BaseInstance::baseJar() const
{
I_D(BaseInstance);
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 443b0eaa..d38ae409 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -190,6 +190,8 @@ public:
bool canLaunch() const;
+ virtual bool reload();
+
signals:
/*!
* \brief Signal emitted when properties relevant to the instance view change
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
index 3e18d17f..6f238c21 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/ForgeInstaller.cpp
@@ -14,7 +14,7 @@
*/
#include "ForgeInstaller.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "net/HttpMetaCache.h"
#include <quazip.h>
@@ -33,7 +33,7 @@
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
{
- std::shared_ptr<OneSixVersion> newVersion;
+ std::shared_ptr<VersionFinal> newVersion;
m_universal_url = universal_url;
QuaZip zip(filename);
@@ -66,7 +66,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
// read the forge version info
{
- newVersion = OneSixVersion::fromJson(versionInfoVal.toObject());
+ newVersion = VersionFinal::fromJson(versionInfoVal.toObject());
if (!newVersion)
return;
}
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
index c5052092..df029f38 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/ForgeInstaller.h
@@ -20,7 +20,7 @@
#include <QString>
#include <memory>
-class OneSixVersion;
+class VersionFinal;
class ForgeInstaller : public BaseInstaller
{
@@ -33,7 +33,7 @@ public:
private:
// the version, read from the installer
- std::shared_ptr<OneSixVersion> m_forge_version;
+ std::shared_ptr<VersionFinal> m_forge_version;
QString internalPath;
QString finalPath;
QString realVersionId;
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
index 126027fb..bb4b07ca 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/LiteLoaderInstaller.cpp
@@ -20,7 +20,7 @@
#include "logger/QsLog.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
@@ -69,7 +69,7 @@ bool LiteLoaderInstaller::add(OneSixInstance *to)
obj.insert("+libraries", libraries);
obj.insert("name", QString("LiteLoader"));
obj.insert("fileId", id());
- obj.insert("version", to->intendedVersionId());
+ obj.insert("version", m_version->version);
obj.insert("mcVersion", to->intendedVersionId());
QFile file(filename(to->instanceRoot()));
diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp
new file mode 100644
index 00000000..25cc2de3
--- /dev/null
+++ b/logic/MMCJson.cpp
@@ -0,0 +1,61 @@
+#include "MMCJson.h"
+#include <QString>
+#include <math.h>
+
+bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
+{
+ if (!val.isBool())
+ throw JSONValidationError(what + " is not boolean");
+ return val.isBool();
+}
+
+QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
+{
+ if(val.isNull())
+ throw JSONValidationError(what + " does not exist");
+ return val;
+}
+
+QJsonArray MMCJson::ensureArray(const QJsonValue val, const QString what)
+{
+ if (!val.isArray())
+ throw JSONValidationError(what + " is not an array");
+ return val.toArray();
+}
+
+double MMCJson::ensureDouble(const QJsonValue val, const QString what)
+{
+ if (!val.isDouble())
+ throw JSONValidationError(what + " is not a number");
+ double ret = val.toDouble();
+}
+
+int MMCJson::ensureInteger(const QJsonValue val, const QString what)
+{
+ double ret = ensureDouble(val, what);
+ if (fmod(ret, 1) != 0)
+ throw JSONValidationError(what + " is not an integer");
+ return ret;
+}
+
+QJsonObject MMCJson::ensureObject(const QJsonValue val, const QString what)
+{
+ if (!val.isObject())
+ throw JSONValidationError(what + " is not an object");
+ return val.toObject();
+}
+
+QJsonObject MMCJson::ensureObject(const QJsonDocument val, const QString what)
+{
+ if (!val.isObject())
+ throw JSONValidationError(what + " is not an object");
+ return val.object();
+}
+
+QString MMCJson::ensureString(const QJsonValue val, const QString what)
+{
+ if (!val.isString())
+ throw JSONValidationError(what + " is not a string");
+ return val.toString();
+}
+
diff --git a/logic/MMCJson.h b/logic/MMCJson.h
new file mode 100644
index 00000000..71ded435
--- /dev/null
+++ b/logic/MMCJson.h
@@ -0,0 +1,46 @@
+/**
+ * Some de-bullshitting for Qt JSON failures.
+ *
+ * Simple exception-throwing
+ */
+
+#pragma once
+#include <QJsonValue>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include "MMCError.h"
+
+class JSONValidationError : public MMCError
+{
+public:
+ JSONValidationError(QString cause) : MMCError(cause) {};
+ virtual ~JSONValidationError() noexcept {}
+};
+
+namespace MMCJson
+{
+/// make sure the value exists. throw otherwise.
+QJsonValue ensureExists(QJsonValue val, const QString what = "value");
+
+/// make sure the value is converted into an object. throw otherwise.
+QJsonObject ensureObject(const QJsonValue val, const QString what = "value");
+
+/// make sure the document is converted into an object. throw otherwise.
+QJsonObject ensureObject(const QJsonDocument val, const QString what = "value");
+
+/// make sure the value is converted into an array. throw otherwise.
+QJsonArray ensureArray(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a string. throw otherwise.
+QString ensureString(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a boolean. throw otherwise.
+bool ensureBoolean(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into an integer. throw otherwise.
+int ensureInteger(const QJsonValue val, QString what = "value");
+
+/// make sure the value is converted into a double precision floating number. throw otherwise.
+double ensureDouble(const QJsonValue val, QString what = "value");
+}
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index 89cd71ed..effa1095 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -49,9 +49,11 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
#endif
// export some infos
- env.insert("INST_NAME", inst->name());
- env.insert("INST_ID", inst->id());
- env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath());
+ auto variables = getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ env.insert(it.key(), it.value());
+ }
this->setProcessEnvironment(env);
m_prepostlaunchprocess.setProcessEnvironment(env);
@@ -63,10 +65,10 @@ MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst)
// Log prepost launch command output (can be disabled.)
if (m_instance->settings().get("LogPrePostOutput").toBool())
{
- connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError,
- this, &MinecraftProcess::on_prepost_stdErr);
- connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput,
- this, &MinecraftProcess::on_prepost_stdOut);
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardError, this,
+ &MinecraftProcess::on_prepost_stdErr);
+ connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this,
+ &MinecraftProcess::on_prepost_stdOut);
}
}
@@ -79,10 +81,10 @@ void MinecraftProcess::setWorkdir(QString path)
QString MinecraftProcess::censorPrivateInfo(QString in)
{
- if(!m_session)
+ if (!m_session)
return in;
- if(m_session->session != "-")
+ if (m_session->session != "-")
in.replace(m_session->session, "<SESSION ID>");
in.replace(m_session->access_token, "<ACCESS TOKEN>");
in.replace(m_session->client_token, "<CLIENT TOKEN>");
@@ -113,7 +115,7 @@ MessageLevel::Enum MinecraftProcess::guessLevel(const QString &line, MessageLeve
level = MessageLevel::Fatal;
if (line.contains("[DEBUG]"))
level = MessageLevel::Debug;
- if(line.contains("overwriting existing"))
+ if (line.contains("overwriting existing"))
level = MessageLevel::Fatal;
return level;
}
@@ -139,17 +141,15 @@ MessageLevel::Enum MinecraftProcess::getLevel(const QString &levelName)
return MessageLevel::Message;
}
-void MinecraftProcess::logOutput(const QStringList &lines,
- MessageLevel::Enum defaultLevel,
+void MinecraftProcess::logOutput(const QStringList &lines, MessageLevel::Enum defaultLevel,
bool guessLevel, bool censor)
{
for (int i = 0; i < lines.size(); ++i)
logOutput(lines[i], defaultLevel, guessLevel, censor);
}
-void MinecraftProcess::logOutput(QString line,
- MessageLevel::Enum defaultLevel,
- bool guessLevel, bool censor)
+void MinecraftProcess::logOutput(QString line, MessageLevel::Enum defaultLevel, bool guessLevel,
+ bool censor)
{
MessageLevel::Enum level = defaultLevel;
@@ -251,33 +251,7 @@ void MinecraftProcess::finish(int code, ExitStatus status)
m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code));
// run post-exit
- QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
- if (!postlaunch_cmd.isEmpty())
- {
- emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
- m_prepostlaunchprocess.start(postlaunch_cmd);
- m_prepostlaunchprocess.waitForFinished();
- // Flush console window
- if (!m_err_leftover.isEmpty())
- {
- logOutput(m_err_leftover, MessageLevel::PrePost);
- m_err_leftover.clear();
- }
- if (!m_out_leftover.isEmpty())
- {
- logOutput(m_out_leftover, MessageLevel::PrePost);
- m_out_leftover.clear();
- }
- if (m_prepostlaunchprocess.exitStatus() != NormalExit)
- {
- emit log(tr("Post-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
- MessageLevel::Error);
- emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
- m_prepostlaunchprocess.exitStatus());
- }
- else
- emit log(tr("Post-Launch command ran successfully.\n\n"));
- }
+ postLaunch();
m_instance->cleanupAfterRun();
emit ended(m_instance, code, status);
}
@@ -288,14 +262,12 @@ void MinecraftProcess::killMinecraft()
kill();
}
-void MinecraftProcess::arm()
+bool MinecraftProcess::preLaunch()
{
- emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
- emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
-
QString prelaunch_cmd = m_instance->settings().get("PreLaunchCommand").toString();
if (!prelaunch_cmd.isEmpty())
{
+ prelaunch_cmd = substituteVariables(prelaunch_cmd);
// Launch
emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
m_prepostlaunchprocess.start(prelaunch_cmd);
@@ -315,46 +287,129 @@ void MinecraftProcess::arm()
// Process return values
if (m_prepostlaunchprocess.exitStatus() != NormalExit)
{
- emit log(tr("Pre-Launch command failed with code %1.\n\n").arg(m_prepostlaunchprocess.exitCode()),
+ emit log(tr("Pre-Launch command failed with code %1.\n\n")
+ .arg(m_prepostlaunchprocess.exitCode()),
MessageLevel::Fatal);
m_instance->cleanupAfterRun();
emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
m_prepostlaunchprocess.exitStatus());
- return;
+ return false;
}
else
emit log(tr("Pre-Launch command ran successfully.\n\n"));
+
+ return m_instance->reload();
+ }
+ return true;
+}
+bool MinecraftProcess::postLaunch()
+{
+ QString postlaunch_cmd = m_instance->settings().get("PostExitCommand").toString();
+ if (!postlaunch_cmd.isEmpty())
+ {
+ postlaunch_cmd = substituteVariables(postlaunch_cmd);
+ emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
+ m_prepostlaunchprocess.start(postlaunch_cmd);
+ m_prepostlaunchprocess.waitForFinished();
+ // Flush console window
+ if (!m_err_leftover.isEmpty())
+ {
+ logOutput(m_err_leftover, MessageLevel::PrePost);
+ m_err_leftover.clear();
+ }
+ if (!m_out_leftover.isEmpty())
+ {
+ logOutput(m_out_leftover, MessageLevel::PrePost);
+ m_out_leftover.clear();
+ }
+ if (m_prepostlaunchprocess.exitStatus() != NormalExit)
+ {
+ emit log(tr("Post-Launch command failed with code %1.\n\n")
+ .arg(m_prepostlaunchprocess.exitCode()),
+ MessageLevel::Error);
+ emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
+ m_prepostlaunchprocess.exitStatus());
+ }
+ else
+ emit log(tr("Post-Launch command ran successfully.\n\n"));
+
+ return m_instance->reload();
}
+ return true;
+}
- m_instance->setLastLaunch();
- auto &settings = m_instance->settings();
+QMap<QString, QString> MinecraftProcess::getVariables() const
+{
+ QMap<QString, QString> out;
+ out.insert("INST_NAME", m_instance->name());
+ out.insert("INST_ID", m_instance->id());
+ out.insert("INST_DIR", QDir(m_instance->instanceRoot()).absolutePath());
+ out.insert("INST_MC_DIR", QDir(m_instance->minecraftRoot()).absolutePath());
+ out.insert("INST_JAVA", m_instance->settings().get("JavaPath").toString());
+ out.insert("INST_JAVA_ARGS", javaArguments().join(' '));
+ return out;
+}
+QString MinecraftProcess::substituteVariables(const QString &cmd) const
+{
+ QString out = cmd;
+ auto variables = getVariables();
+ for (auto it = variables.begin(); it != variables.end(); ++it)
+ {
+ out.replace("$" + it.key(), it.value());
+ }
+ auto env = QProcessEnvironment::systemEnvironment();
+ for (auto var : env.keys())
+ {
+ out.replace("$" + var, env.value(var));
+ }
+ return out;
+}
- //////////// java arguments ////////////
+QStringList MinecraftProcess::javaArguments() const
+{
QStringList args;
+
+ // custom args go first. we want to override them if we have our own here.
+ args.append(m_instance->extraArguments());
+
+// OSX dock icon and name
+#ifdef OSX
+ args << "-Xdock:icon=icon.png";
+ args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
+#endif
+
+// HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
+#ifdef Q_OS_WIN32
+ args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
+ "minecraft.exe.heapdump");
+#endif
+
+ args << QString("-Xms%1m").arg(m_instance->settings().get("MinMemAlloc").toInt());
+ args << QString("-Xmx%1m").arg(m_instance->settings().get("MaxMemAlloc").toInt());
+ args << QString("-XX:PermSize=%1m").arg(m_instance->settings().get("PermGen").toInt());
+ args << "-Duser.language=en";
+ if (!m_nativeFolder.isEmpty())
+ args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
+ args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
+
+ return args;
+}
+
+void MinecraftProcess::arm()
+{
+ emit log("MultiMC version: " + MMC->version().toString() + "\n\n");
+ emit log("Minecraft folder is:\n" + workingDirectory() + "\n\n");
+
+ if (!preLaunch())
{
- // custom args go first. we want to override them if we have our own here.
- args.append(m_instance->extraArguments());
-
- // OSX dock icon and name
- #ifdef OSX
- args << "-Xdock:icon=icon.png";
- args << QString("-Xdock:name=\"%1\"").arg(m_instance->windowTitle());
- #endif
-
- // HACK: Stupid hack for Intel drivers. See: https://mojang.atlassian.net/browse/MCL-767
- #ifdef Q_OS_WIN32
- args << QString("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_"
- "minecraft.exe.heapdump");
- #endif
-
- args << QString("-Xms%1m").arg(settings.get("MinMemAlloc").toInt());
- args << QString("-Xmx%1m").arg(settings.get("MaxMemAlloc").toInt());
- args << QString("-XX:PermSize=%1m").arg(settings.get("PermGen").toInt());
- if(!m_nativeFolder.isEmpty())
- args << QString("-Djava.library.path=%1").arg(m_nativeFolder);
- args << "-jar" << PathCombine(MMC->bin(), "jars", "NewLaunch.jar");
+ return;
}
+ m_instance->setLastLaunch();
+ auto &settings = m_instance->settings();
+
+ QStringList args = javaArguments();
+
QString JavaPath = m_instance->settings().get("JavaPath").toString();
emit log("Java path is:\n" + JavaPath + "\n\n");
QString allArgs = args.join(", ");
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
index 56340962..d91dad56 100644
--- a/logic/MinecraftProcess.h
+++ b/logic/MinecraftProcess.h
@@ -131,6 +131,13 @@ protected:
QString launchScript;
QString m_nativeFolder;
+ bool preLaunch();
+ bool postLaunch();
+ QMap<QString, QString> getVariables() const;
+ QString substituteVariables(const QString &cmd) const;
+
+ QStringList javaArguments() const;
+
protected
slots:
void finish(int, QProcess::ExitStatus status);
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 07b123b6..172830bb 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -1,6 +1,6 @@
#include "OneSixFTBInstance.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "tasks/SequentialTask.h"
#include "ForgeInstaller.h"
@@ -10,76 +10,6 @@
#include "MultiMC.h"
#include "pathutils.h"
-class OneSixFTBInstanceForge : public Task
-{
- Q_OBJECT
-public:
- explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) :
- Task(parent), instance(inst), version("Forge " + version)
- {
- }
-
- void executeTask()
- {
- for (int i = 0; i < MMC->forgelist()->count(); ++i)
- {
- if (MMC->forgelist()->at(i)->name() == version)
- {
- forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(MMC->forgelist()->at(i));
- break;
- }
- }
- if (!forgeVersion)
- {
- emitFailed(QString("Couldn't find forge version ") + version );
- return;
- }
- entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
- if (entry->stale)
- {
- setStatus(tr("Downloading Forge..."));
- fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
- connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);});
- connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge);
- connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); });
- fjob->start();
- }
- else
- {
- installForge();
- }
- }
-
-private
-slots:
- void installForge()
- {
- setStatus(tr("Installing Forge..."));
- QString forgePath = entry->getFullPath();
- ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!instance->reloadVersion())
- {
- emitFailed(tr("Couldn't load the version config"));
- return;
- }
- auto version = instance->getFullVersion();
- if (!forge.add(instance))
- {
- emitFailed(tr("Couldn't install Forge"));
- return;
- }
- emitSucceeded();
- }
-
-private:
- OneSixFTBInstance *instance;
- QString version;
- ForgeVersionPtr forgeVersion;
- MetaEntryPtr entry;
- NetJob *fjob;
-};
-
OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) :
OneSixInstance(rootDir, settings, parent)
{
@@ -87,7 +17,14 @@ OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *set
void OneSixFTBInstance::init()
{
- reloadVersion();
+ try
+ {
+ reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
+ }
}
void OneSixFTBInstance::copy(const QDir &newDir)
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 148fbc09..ccc25845 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -19,7 +19,7 @@
#include "OneSixInstance_p.h"
#include "OneSixUpdate.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "pathutils.h"
#include "logger/QsLog.h"
#include "assets/AssetsUtils.h"
@@ -27,6 +27,7 @@
#include "icons/IconList.h"
#include "MinecraftProcess.h"
#include "gui/dialogs/OneSixModEditDialog.h"
+#include <MMCError.h>
OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
: BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
@@ -34,15 +35,23 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings,
I_D(OneSixInstance);
d->m_settings->registerSetting("IntendedVersion", "");
d->m_settings->registerSetting("ShouldUpdate", false);
- d->version.reset(new OneSixVersion(this, this));
- d->vanillaVersion.reset(new OneSixVersion(this, this));
+ d->version.reset(new VersionFinal(this, this));
+ d->vanillaVersion.reset(new VersionFinal(this, this));
}
void OneSixInstance::init()
{
+ // FIXME: why is this decided here? what does this even mean?
if (QDir(instanceRoot()).exists("version.json"))
{
- reloadVersion();
+ try
+ {
+ reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
+ }
}
else
{
@@ -79,7 +88,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result;
}
-QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
+QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
@@ -316,25 +325,26 @@ QString OneSixInstance::currentVersionId() const
return intendedVersionId();
}
-bool OneSixInstance::reloadVersion(QWidget *widgetParent)
+void OneSixInstance::reloadVersion()
{
I_D(OneSixInstance);
- bool ret = d->version->reload(widgetParent, false, externalPatches());
- if (ret)
- {
- ret = d->vanillaVersion->reload(widgetParent, true, externalPatches());
- }
- if (ret)
+ try
{
+ d->version->reload(false, externalPatches());
+ d->vanillaVersion->reload(true, externalPatches());
d->m_flags.remove(VersionBrokenFlag);
emit versionReloaded();
}
- else
+ catch(MMCError & error)
{
+ d->version->clear();
+ d->vanillaVersion->clear();
d->m_flags.insert(VersionBrokenFlag);
+ //TODO: rethrow to show some error message(s)?
+ emit versionReloaded();
+ throw;
}
- return ret;
}
void OneSixInstance::clearVersion()
@@ -345,13 +355,13 @@ void OneSixInstance::clearVersion()
emit versionReloaded();
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
+std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const
{
I_D(const OneSixInstance);
return d->version;
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getVanillaVersion() const
+std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const
{
I_D(const OneSixInstance);
return d->vanillaVersion;
@@ -413,6 +423,23 @@ bool OneSixInstance::providesVersionFile() const
return false;
}
+bool OneSixInstance::reload()
+{
+ if(BaseInstance::reload())
+ {
+ try
+ {
+ reloadVersion();
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+ }
+ return false;
+}
+
QString OneSixInstance::loaderModsDir() const
{
return PathCombine(minecraftRoot(), "mods");
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index 06fd9de3..7a049922 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -17,7 +17,7 @@
#include "BaseInstance.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "ModList.h"
class OneSixInstance : public BaseInstance
@@ -53,14 +53,18 @@ public:
virtual QDialog *createModEditDialog(QWidget *parent) override;
- /// reload the full version json files. return true on success!
- bool reloadVersion(QWidget *widgetParent = 0);
+ /**
+ * reload the full version json files. return true on success!
+ *
+ * throws various exceptions :3
+ */
+ void reloadVersion();
/// clears all version information in preparation for an update
void clearVersion();
/// get the current full version info
- std::shared_ptr<OneSixVersion> getFullVersion() const;
+ std::shared_ptr<VersionFinal> getFullVersion() const;
/// gets the current version info, but only for version.json
- std::shared_ptr<OneSixVersion> getVanillaVersion() const;
+ std::shared_ptr<VersionFinal> getVanillaVersion() const;
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
@@ -75,10 +79,12 @@ public:
virtual QStringList externalPatches() const;
virtual bool providesVersionFile() const;
+ bool reload() override;
+
signals:
void versionReloaded();
private:
QStringList processMinecraftArgs(AuthSessionPtr account);
- QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
+ QDir reconstructAssets(std::shared_ptr<VersionFinal> version);
};
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index 0cc46f33..2dffa62c 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -16,13 +16,13 @@
#pragma once
#include "BaseInstance_p.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "ModList.h"
struct OneSixInstancePrivate : public BaseInstancePrivate
{
- std::shared_ptr<OneSixVersion> version;
- std::shared_ptr<OneSixVersion> vanillaVersion;
+ std::shared_ptr<VersionFinal> version;
+ std::shared_ptr<VersionFinal> vanillaVersion;
std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> resource_pack_list;
};
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
index 371ca6f4..3bd21c51 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/OneSixLibrary.h
@@ -26,6 +26,9 @@
class Rule;
+class OneSixLibrary;
+typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
+
class OneSixLibrary
{
private:
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index f87c65e7..65f30cda 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -25,7 +25,7 @@
#include "BaseInstance.h"
#include "lists/MinecraftVersionList.h"
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
#include "net/ForgeMirrors.h"
@@ -48,7 +48,7 @@ void OneSixUpdate::executeTask()
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
{
- emitFailed("Failed to create bin folder.");
+ emitFailed(tr("Failed to create folder for minecraft binaries."));
return;
}
@@ -60,7 +60,7 @@ void OneSixUpdate::executeTask()
if (targetVersion == nullptr)
{
// don't do anything if it was invalid
- emitFailed("The specified Minecraft version is invalid. Choose a different one.");
+ emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
return;
}
versionFileStart();
@@ -108,20 +108,19 @@ void OneSixUpdate::versionFileFinished()
QSaveFile vfile1(version1);
if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
{
- emitFailed("Can't open " + version1 + " for writing.");
+ emitFailed(tr("Can't open %1 for writing.").arg(version1));
return;
}
auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
qint64 actual = 0;
if ((actual = vfile1.write(data)) != data.size())
{
- emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " +
- data.size() + '.');
+ emitFailed(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
return;
}
if (!vfile1.commit())
{
- emitFailed("Can't commit changes to " + version1);
+ emitFailed(tr("Can't commit changes to %1").arg(version1));
return;
}
}
@@ -136,21 +135,20 @@ void OneSixUpdate::versionFileFinished()
{
finfo.remove();
}
- inst->reloadVersion();
-
+ // NOTE: Version is reloaded in jarlibStart
jarlibStart();
}
void OneSixUpdate::versionFileFailed()
{
- emitFailed("Failed to download the version description. Try again.");
+ emitFailed(tr("Failed to download the version description. Try again."));
}
void OneSixUpdate::assetIndexStart()
{
setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
QString assetName = version->assets;
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
QString localPath = assetName + ".json";
@@ -174,13 +172,13 @@ void OneSixUpdate::assetIndexFinished()
AssetsIndex index;
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
QString assetName = version->assets;
QString asset_fname = "assets/indexes/" + assetName + ".json";
if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index))
{
- emitFailed("Failed to read the assets index!");
+ emitFailed(tr("Failed to read the assets index!"));
}
QList<Md5EtagDownloadPtr> dls;
@@ -216,7 +214,7 @@ void OneSixUpdate::assetIndexFinished()
void OneSixUpdate::assetIndexFailed()
{
- emitFailed("Failed to download the assets index!");
+ emitFailed(tr("Failed to download the assets index!"));
}
void OneSixUpdate::assetsFinished()
@@ -226,7 +224,7 @@ void OneSixUpdate::assetsFinished()
void OneSixUpdate::assetsFailed()
{
- emitFailed("Failed to download assets!");
+ emitFailed(tr("Failed to download assets!"));
}
void OneSixUpdate::jarlibStart()
@@ -234,16 +232,23 @@ void OneSixUpdate::jarlibStart()
setStatus(tr("Getting the library files from Mojang..."));
QLOG_INFO() << m_inst->name() << ": downloading libraries";
OneSixInstance *inst = (OneSixInstance *)m_inst;
- bool successful = inst->reloadVersion();
- if (!successful)
+ try
+ {
+ inst->reloadVersion();
+ }
+ catch(MMCError & e)
+ {
+ emitFailed(e.cause());
+ return;
+ }
+ catch(...)
{
- emitFailed("Failed to load the version description file. It might be "
- "corrupted, missing or simply too new.");
+ emitFailed(tr("Failed to load the version description file for reasons unknown."));
return;
}
// Build a list of URLs that will need to be downloaded.
- std::shared_ptr<OneSixVersion> version = inst->getFullVersion();
+ std::shared_ptr<VersionFinal> version = inst->getFullVersion();
// minecraft.jar for this version
{
QString version_id = version->id;
@@ -326,6 +331,5 @@ void OneSixUpdate::jarlibFailed()
{
QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n");
- emitFailed("Failed to download the following files:\n" + failed_all +
- "\n\nPlease try again.");
+ emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
}
diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp
deleted file mode 100644
index 06e748bd..00000000
--- a/logic/OneSixVersion.cpp
+++ /dev/null
@@ -1,221 +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 "OneSixVersion.h"
-
-#include <QDebug>
-#include <QFile>
-
-#include "OneSixVersionBuilder.h"
-
-OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
- : QAbstractListModel(parent), m_instance(instance)
-{
- clear();
-}
-
-bool OneSixVersion::reload(QWidget *widgetParent, const bool onlyVanilla, const QStringList &external)
-{
- beginResetModel();
- bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, onlyVanilla, external);
- endResetModel();
- return ret;
-}
-
-void OneSixVersion::clear()
-{
- beginResetModel();
- id.clear();
- time.clear();
- releaseTime.clear();
- type.clear();
- assets.clear();
- processArguments.clear();
- minecraftArguments.clear();
- minimumLauncherVersion = 0xDEADBEAF;
- mainClass.clear();
- libraries.clear();
- tweakers.clear();
- versionFiles.clear();
- endResetModel();
-}
-
-void OneSixVersion::dump() const
-{
- qDebug().nospace() << "OneSixVersion("
- << "\n\tid=" << id
- << "\n\ttime=" << time
- << "\n\treleaseTime=" << releaseTime
- << "\n\ttype=" << type
- << "\n\tassets=" << assets
- << "\n\tprocessArguments=" << processArguments
- << "\n\tminecraftArguments=" << minecraftArguments
- << "\n\tminimumLauncherVersion=" << minimumLauncherVersion
- << "\n\tmainClass=" << mainClass
- << "\n\tlibraries=";
- for (auto lib : libraries)
- {
- qDebug().nospace() << "\n\t\t" << lib.get();
- }
- qDebug().nospace() << "\n)";
-}
-
-bool OneSixVersion::canRemove(const int index) const
-{
- if (index < versionFiles.size())
- {
- return versionFiles.at(index).id != "org.multimc.version.json";
- }
- return false;
-}
-
-QString OneSixVersion::versionFileId(const int index) const
-{
- if (index < 0 || index >= versionFiles.size())
- {
- return QString();
- }
- return versionFiles.at(index).id;
-}
-
-bool OneSixVersion::remove(const int index)
-{
- if (canRemove(index))
- {
- return QFile::remove(versionFiles.at(index).filename);
- }
- return false;
-}
-
-QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && !lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-
-QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs()
-{
- QList<std::shared_ptr<OneSixLibrary> > output;
- for (auto lib : libraries)
- {
- if (lib->isActive() && lib->isNative())
- {
- output.append(lib);
- }
- }
- return output;
-}
-
-std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
-{
- std::shared_ptr<OneSixVersion> version(new OneSixVersion(0));
- if (OneSixVersionBuilder::read(version.get(), obj))
- {
- return version;
- }
- return 0;
-}
-
-QVariant OneSixVersion::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
-
- int row = index.row();
- int column = index.column();
-
- if (row < 0 || row >= versionFiles.size())
- return QVariant();
-
- if (role == Qt::DisplayRole)
- {
- switch (column)
- {
- case 0:
- return versionFiles.at(row).name;
- case 1:
- return versionFiles.at(row).version;
- default:
- return QVariant();
- }
- }
- return QVariant();
-}
-
-QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal)
- {
- if (role == Qt::DisplayRole)
- {
- switch (section)
- {
- case 0:
- return tr("Name");
- case 1:
- return tr("Version");
- default:
- return QVariant();
- }
- }
- }
- return QVariant();
-}
-
-Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return Qt::NoItemFlags;
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-}
-
-int OneSixVersion::rowCount(const QModelIndex &parent) const
-{
- return versionFiles.size();
-}
-
-int OneSixVersion::columnCount(const QModelIndex &parent) const
-{
- return 2;
-}
-
-QDebug operator<<(QDebug &dbg, const OneSixVersion *version)
-{
- version->dump();
- return dbg.maybeSpace();
-}
-QDebug operator<<(QDebug &dbg, const OneSixLibrary *library)
-{
- dbg.nospace() << "OneSixLibrary("
- << "\n\t\t\trawName=" << library->rawName()
- << "\n\t\t\tname=" << library->name()
- << "\n\t\t\tversion=" << library->version()
- << "\n\t\t\ttype=" << library->type()
- << "\n\t\t\tisActive=" << library->isActive()
- << "\n\t\t\tisNative=" << library->isNative()
- << "\n\t\t\tdownloadUrl=" << library->downloadUrl()
- << "\n\t\t\tstoragePath=" << library->storagePath()
- << "\n\t\t\tabsolutePath=" << library->absoluteUrl()
- << "\n\t\t\thint=" << library->hint();
- dbg.nospace() << "\n\t\t)";
- return dbg.maybeSpace();
-}
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
index f6917697..8eacbce4 100644
--- a/logic/OneSixVersionBuilder.cpp
+++ b/logic/OneSixVersionBuilder.cpp
@@ -26,806 +26,37 @@
#include <QDir>
#include <QDebug>
-#include "OneSixVersion.h"
+#include "VersionFinal.h"
#include "OneSixInstance.h"
#include "OneSixRule.h"
+#include "VersionFile.h"
+#include "MMCJson.h"
#include "modutils.h"
#include "logger/QsLog.h"
-#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
-
-struct VersionFile
-{
- int order;
- QString name;
- QString fileId;
- QString version;
- // TODO use the mcVersion to determine if a version file should be removed on update
- QString mcVersion;
- QString filename;
- // TODO requirements
- // QMap<QString, QString> requirements;
- QString id;
- QString mainClass;
- QString overwriteMinecraftArguments;
- QString addMinecraftArguments;
- QString removeMinecraftArguments;
- QString processArguments;
- QString type;
- QString releaseTime;
- QString time;
- QString assets;
- int minimumLauncherVersion = -1;
-
- bool shouldOverwriteTweakers = false;
- QStringList overwriteTweakers;
- QStringList addTweakers;
- QStringList removeTweakers;
-
- struct Library
- {
- QString name;
- QString url;
- QString hint;
- QString absoluteUrl;
- bool applyExcludes = false;
- QStringList excludes;
- bool applyNatives = false;
- QList<QPair<OpSys, QString>> natives;
- bool applyRules = false;
- QList<std::shared_ptr<Rule>> rules;
-
- // user for '+' libraries
- enum InsertType
- {
- Apply,
- Append,
- Prepend,
- Replace
- };
- InsertType insertType = Append;
- QString insertData;
- enum DependType
- {
- Soft,
- Hard
- };
- DependType dependType = Soft;
- };
- bool shouldOverwriteLibs = false;
- QList<Library> overwriteLibs;
- QList<Library> addLibs;
- QList<QString> removeLibs;
-
- enum ApplyError
- {
- LauncherVersionError,
- OtherError,
- NoApplyError
- };
-
- static Library fromLibraryJson(const QJsonObject &libObj, const QString &filename,
- bool &isError)
- {
- isError = true;
- Library out;
- if (!libObj.contains("name"))
- {
- QLOG_ERROR() << filename << "contains a library that doesn't have a 'name' field";
- return out;
- }
- out.name = libObj.value("name").toString();
-
- auto readString = [libObj, filename](const QString &key, QString &variable)
- {
- if (libObj.contains(key))
- {
- QJsonValue val = libObj.value(key);
- if (!val.isString())
- {
- QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
- }
- else
- {
- variable = val.toString();
- }
- }
- };
-
- readString("url", out.url);
- readString("MMC-hint", out.hint);
- readString("MMC-absulute_url", out.absoluteUrl);
- readString("MMC-absoluteUrl", out.absoluteUrl);
- if (libObj.contains("extract"))
- {
- if (!libObj.value("extract").isObject())
- {
- QLOG_ERROR()
- << filename
- << "contains a library with an 'extract' field that's not an object";
- return out;
- }
- QJsonObject extractObj = libObj.value("extract").toObject();
- if (!extractObj.contains("exclude") || !extractObj.value("exclude").isArray())
- {
- QLOG_ERROR() << filename
- << "contains a library with an invalid 'extract' field";
- return out;
- }
- out.applyExcludes = true;
- QJsonArray excludeArray = extractObj.value("exclude").toArray();
- for (auto excludeVal : excludeArray)
- {
- if (!excludeVal.isString())
- {
- QLOG_WARN() << filename << "contains a library that contains an 'extract' "
- "field that contains an invalid 'exclude' entry "
- "(skipping)";
- }
- else
- {
- out.excludes.append(excludeVal.toString());
- }
- }
- }
- if (libObj.contains("natives"))
- {
- if (!libObj.value("natives").isObject())
- {
- QLOG_ERROR()
- << filename
- << "contains a library with a 'natives' field that's not an object";
- return out;
- }
- out.applyNatives = true;
- QJsonObject nativesObj = libObj.value("natives").toObject();
- for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
- {
- if (!it.value().isString())
- {
- QLOG_WARN() << filename << "contains an invalid native (skipping)";
- }
- OpSys opSys = OpSys_fromString(it.key());
- if (opSys != Os_Other)
- {
- out.natives.append(qMakePair(opSys, it.value().toString()));
- }
- }
- }
- if (libObj.contains("rules"))
- {
- out.applyRules = true;
- out.rules = rulesFromJsonV4(libObj);
- }
- isError = false;
- return out;
- }
- static VersionFile fromJson(const QJsonDocument &doc, const QString &filename,
- const bool requireOrder, bool &isError, const OneSixVersionBuilder::ParseFlags flags = OneSixVersionBuilder::NoFlags)
- {
- VersionFile out;
- isError = true;
- if (doc.isEmpty() || doc.isNull())
- {
- QLOG_ERROR() << filename << "is empty or null";
- return out;
- }
- if (!doc.isObject())
- {
- QLOG_ERROR() << "The root of" << filename << "is not an object";
- return out;
- }
-
- QJsonObject root = doc.object();
-
- if (requireOrder)
- {
- if (root.contains("order"))
- {
- if (root.value("order").isDouble())
- {
- out.order = root.value("order").toDouble();
- }
- else
- {
- QLOG_ERROR() << "'order' field contains an invalid value in" << filename;
- return out;
- }
- }
- else
- {
- QLOG_ERROR() << filename << "doesn't contain an order field";
- }
- }
-
- out.name = root.value("name").toString();
- out.fileId = root.value("fileId").toString();
- out.version = root.value("version").toString();
- out.mcVersion = root.value("mcVersion").toString();
- out.filename = filename;
-
- auto readString = [root, filename](const QString &key, QString &variable)
- {
- if (root.contains(key))
- {
- QJsonValue val = root.value(key);
- if (!val.isString())
- {
- QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
- }
- else
- {
- variable = val.toString();
- }
- }
- };
-
- if (!(flags & OneSixVersionBuilder::IsFTBPackJson))
- {
- readString("id", out.id);
- }
- readString("mainClass", out.mainClass);
- readString("processArguments", out.processArguments);
- readString("minecraftArguments", out.overwriteMinecraftArguments);
- readString("+minecraftArguments", out.addMinecraftArguments);
- readString("-minecraftArguments", out.removeMinecraftArguments);
- readString("type", out.type);
- readString("releaseTime", out.releaseTime);
- readString("time", out.time);
- readString("assets", out.assets);
- if (root.contains("minimumLauncherVersion"))
- {
- QJsonValue val = root.value("minimumLauncherVersion");
- if (!val.isDouble())
- {
- QLOG_WARN() << "minimumLauncherVersion is not an int in" << filename
- << "(skipping)";
- }
- else
- {
- out.minimumLauncherVersion = val.toDouble();
- }
- }
-
- if (root.contains("tweakers"))
- {
- QJsonValue tweakersVal = root.value("tweakers");
- if (!tweakersVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a 'tweakers' field, but it's not an array";
- return out;
- }
- out.shouldOverwriteTweakers = true;
- QJsonArray tweakers = root.value("tweakers").toArray();
- for (auto tweakerVal : tweakers)
- {
- if (!tweakerVal.isString())
- {
- QLOG_ERROR() << filename
- << "contains a 'tweakers' field entry that's not a string";
- return out;
- }
- out.overwriteTweakers.append(tweakerVal.toString());
- }
- }
- if (root.contains("+tweakers"))
- {
- QJsonValue tweakersVal = root.value("+tweakers");
- if (!tweakersVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a '+tweakers' field, but it's not an array";
- return out;
- }
- QJsonArray tweakers = root.value("+tweakers").toArray();
- for (auto tweakerVal : tweakers)
- {
- if (!tweakerVal.isString())
- {
- QLOG_ERROR() << filename
- << "contains a '+tweakers' field entry that's not a string";
- return out;
- }
- out.addTweakers.append(tweakerVal.toString());
- }
- }
- if (root.contains("-tweakers"))
- {
- QJsonValue tweakersVal = root.value("-tweakers");
- if (!tweakersVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a '-tweakers' field, but it's not an array";
- return out;
- }
- out.shouldOverwriteTweakers = true;
- QJsonArray tweakers = root.value("-tweakers").toArray();
- for (auto tweakerVal : tweakers)
- {
- if (!tweakerVal.isString())
- {
- QLOG_ERROR() << filename
- << "contains a '-tweakers' field entry that's not a string";
- return out;
- }
- out.removeTweakers.append(tweakerVal.toString());
- }
- }
-
- if (root.contains("libraries"))
- {
- out.shouldOverwriteLibs = !(flags & OneSixVersionBuilder::IsFTBPackJson);
- QJsonValue librariesVal = root.value("libraries");
- if (!librariesVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a 'libraries' field, but its not an array";
- return out;
- }
- QJsonArray librariesArray = librariesVal.toArray();
- for (auto libVal : librariesArray)
- {
- if (!libVal.isObject())
- {
- QLOG_ERROR() << filename << "contains a library that's not an object";
- return out;
- }
- QJsonObject libObj = libVal.toObject();
- bool error;
- Library lib = fromLibraryJson(libObj, filename, error);
- if (error)
- {
- QLOG_ERROR() << "Error while reading a library entry in" << filename;
- return out;
- }
- if (flags & OneSixVersionBuilder::IsFTBPackJson)
- {
- lib.hint = "local";
- lib.insertType = Library::Prepend;
- out.addLibs.prepend(lib);
- }
- else
- {
- out.overwriteLibs.append(lib);
- }
- }
- }
- if (root.contains("+libraries"))
- {
- QJsonValue librariesVal = root.value("+libraries");
- if (!librariesVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a '+libraries' field, but its not an array";
- return out;
- }
- QJsonArray librariesArray = librariesVal.toArray();
- for (auto libVal : librariesArray)
- {
- if (!libVal.isObject())
- {
- QLOG_ERROR() << filename << "contains a library that's not an object";
- return out;
- }
- QJsonObject libObj = libVal.toObject();
- bool error;
- Library lib = fromLibraryJson(libObj, filename, error);
- if (error)
- {
- QLOG_ERROR() << "Error while reading a library entry in" << filename;
- return out;
- }
- if (!libObj.contains("insert"))
- {
- QLOG_ERROR() << "Missing 'insert' field in '+libraries' field in"
- << filename;
- return out;
- }
- QJsonValue insertVal = libObj.value("insert");
- QString insertString;
- {
- if (insertVal.isString())
- {
- insertString = insertVal.toString();
- }
- else if (insertVal.isObject())
- {
- QJsonObject insertObj = insertVal.toObject();
- if (insertObj.isEmpty())
- {
- QLOG_ERROR() << "One library has an empty insert object in"
- << filename;
- return out;
- }
- insertString = insertObj.keys().first();
- lib.insertData = insertObj.value(insertString).toString();
- }
- }
- if (insertString == "apply")
- {
- lib.insertType = Library::Apply;
- }
- else if (insertString == "prepend")
- {
- lib.insertType = Library::Prepend;
- }
- else if (insertString == "append")
- {
- lib.insertType = Library::Prepend;
- }
- else if (insertString == "replace")
- {
- lib.insertType = Library::Replace;
- }
- else
- {
- QLOG_ERROR() << "A '+' library in" << filename
- << "contains an invalid insert type";
- return out;
- }
- if (libObj.contains("MMC-depend") && libObj.value("MMC-depend").isString())
- {
- const QString dependString = libObj.value("MMC-depend").toString();
- if (dependString == "hard")
- {
- lib.dependType = Library::Hard;
- }
- else if (dependString == "soft")
- {
- lib.dependType = Library::Soft;
- }
- else
- {
- QLOG_ERROR() << "A '+' library in" << filename
- << "contains an invalid depend type";
- return out;
- }
- }
- out.addLibs.append(lib);
- }
- }
- if (root.contains("-libraries"))
- {
- QJsonValue librariesVal = root.value("-libraries");
- if (!librariesVal.isArray())
- {
- QLOG_ERROR() << filename
- << "contains a '-libraries' field, but its not an array";
- return out;
- }
- QJsonArray librariesArray = librariesVal.toArray();
- for (auto libVal : librariesArray)
- {
- if (!libVal.isObject())
- {
- QLOG_ERROR() << filename << "contains a library that's not an object";
- return out;
- }
- QJsonObject libObj = libVal.toObject();
- if (!libObj.contains("name"))
- {
- QLOG_ERROR() << filename << "contains a library without a name";
- return out;
- }
- if (!libObj.value("name").isString())
- {
- QLOG_ERROR() << filename
- << "contains a library without a valid 'name' field";
- return out;
- }
- out.removeLibs.append(libObj.value("name").toString());
- }
- }
-
- isError = false;
- return out;
- }
-
- static std::shared_ptr<OneSixLibrary> createLibrary(const Library &lib)
- {
- std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib.name));
- if (!lib.url.isEmpty())
- {
- out->setBaseUrl(lib.url);
- }
- out->setHint(lib.hint);
- if (!lib.absoluteUrl.isEmpty())
- {
- out->setAbsoluteUrl(lib.absoluteUrl);
- }
- out->setAbsoluteUrl(lib.absoluteUrl);
- out->extract_excludes = lib.excludes;
- for (auto native : lib.natives)
- {
- out->addNative(native.first, native.second);
- }
- out->setRules(lib.rules);
- out->finalize();
- return out;
- }
- int findLibrary(QList<std::shared_ptr<OneSixLibrary>> haystack, const QString &needle)
- {
- for (int i = 0; i < haystack.size(); ++i)
- {
- if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix)
- .indexIn(haystack.at(i)->rawName()) != -1)
- {
- return i;
- }
- }
- return -1;
- }
- ApplyError applyTo(OneSixVersion *version)
- {
- if (minimumLauncherVersion != -1)
- {
- if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
- {
- QLOG_ERROR() << filename << "is for a different launcher version ("
- << minimumLauncherVersion << "), current supported is"
- << CURRENT_MINIMUM_LAUNCHER_VERSION;
- return LauncherVersionError;
- }
- }
-
- if (!version->id.isNull() && !mcVersion.isNull())
- {
- if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard)
- .indexIn(version->id) == -1)
- {
- QLOG_ERROR() << filename << "is for a different version of Minecraft";
- return OtherError;
- }
- }
-
- if (!id.isNull())
- {
- version->id = id;
- }
- if (!mainClass.isNull())
- {
- version->mainClass = mainClass;
- }
- if (!processArguments.isNull())
- {
- version->processArguments = processArguments;
- }
- if (!type.isNull())
- {
- version->type = type;
- }
- if (!releaseTime.isNull())
- {
- version->releaseTime = releaseTime;
- }
- if (!time.isNull())
- {
- version->time = time;
- }
- if (!assets.isNull())
- {
- version->assets = assets;
- }
- if (minimumLauncherVersion >= 0)
- {
- version->minimumLauncherVersion = minimumLauncherVersion;
- }
- if (!overwriteMinecraftArguments.isNull())
- {
- version->minecraftArguments = overwriteMinecraftArguments;
- }
- if (!addMinecraftArguments.isNull())
- {
- version->minecraftArguments += addMinecraftArguments;
- }
- if (!removeMinecraftArguments.isNull())
- {
- version->minecraftArguments.remove(removeMinecraftArguments);
- }
- if (shouldOverwriteTweakers)
- {
- version->tweakers = overwriteTweakers;
- }
- for (auto tweaker : addTweakers)
- {
- version->tweakers += tweaker;
- }
- for (auto tweaker : removeTweakers)
- {
- version->tweakers.removeAll(tweaker);
- }
- if (shouldOverwriteLibs)
- {
- version->libraries.clear();
- for (auto lib : overwriteLibs)
- {
- version->libraries.append(createLibrary(lib));
- }
- }
- for (auto lib : addLibs)
- {
- switch (lib.insertType)
- {
- case Library::Apply:
- {
-
- int index = findLibrary(version->libraries, lib.name);
- if (index >= 0)
- {
- auto library = version->libraries[index];
- if (!lib.url.isNull())
- {
- library->setBaseUrl(lib.url);
- }
- if (!lib.hint.isNull())
- {
- library->setHint(lib.hint);
- }
- if (!lib.absoluteUrl.isNull())
- {
- library->setAbsoluteUrl(lib.absoluteUrl);
- }
- if (lib.applyExcludes)
- {
- library->extract_excludes = lib.excludes;
- }
- if (lib.applyNatives)
- {
- library->clearSuffixes();
- for (auto native : lib.natives)
- {
- library->addNative(native.first, native.second);
- }
- }
- if (lib.applyRules)
- {
- library->setRules(lib.rules);
- }
- library->finalize();
- }
- else
- {
- QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
- }
- break;
- }
- case Library::Append:
- case Library::Prepend:
- {
-
- const int startOfVersion = lib.name.lastIndexOf(':') + 1;
- const int index =
- findLibrary(version->libraries,
- QString(lib.name).replace(startOfVersion, INT_MAX, '*'));
- if (index < 0)
- {
- if (lib.insertType == Library::Append)
- {
- version->libraries.append(createLibrary(lib));
- }
- else
- {
- version->libraries.prepend(createLibrary(lib));
- }
- }
- else
- {
- auto otherLib = version->libraries.at(index);
- const Util::Version ourVersion = lib.name.mid(startOfVersion, INT_MAX);
- const Util::Version otherVersion = otherLib->version();
- // if the existing version is a hard dependency we can either use it or
- // fail, but we can't change it
- if (otherLib->dependType == OneSixLibrary::Hard)
- {
- // we need a higher version, or we're hard to and the versions aren't
- // equal
- if (ourVersion > otherVersion ||
- (lib.dependType == Library::Hard && ourVersion != otherVersion))
- {
- QLOG_ERROR() << "Error resolving library dependencies between"
- << otherLib->rawName() << "and" << lib.name << "in"
- << filename;
- return OtherError;
- }
- else
- {
- // the library is already existing, so we don't have to do anything
- }
- }
- else if (otherLib->dependType == OneSixLibrary::Soft)
- {
- // if we are higher it means we should update
- if (ourVersion > otherVersion)
- {
- auto library = createLibrary(lib);
- if (Util::Version(otherLib->minVersion) < ourVersion)
- {
- library->minVersion = ourVersion.toString();
- }
- version->libraries.replace(index, library);
- }
- else
- {
- // our version is smaller than the existing version, but we require
- // it: fail
- if (lib.dependType == Library::Hard)
- {
- QLOG_ERROR() << "Error resolving library dependencies between"
- << otherLib->rawName() << "and" << lib.name << "in"
- << filename;
- return OtherError;
- }
- }
- }
- }
- break;
- }
- case Library::Replace:
- {
- int index = findLibrary(version->libraries, lib.insertData);
- if (index >= 0)
- {
- version->libraries.replace(index, createLibrary(lib));
- }
- else
- {
- QLOG_WARN() << "Couldn't find" << lib.insertData << "(skipping)";
- }
- break;
- }
- }
- }
- for (auto lib : removeLibs)
- {
- int index = findLibrary(version->libraries, lib);
- if (index >= 0)
- {
- version->libraries.removeAt(index);
- }
- else
- {
- QLOG_WARN() << "Couldn't find" << lib << "(skipping)";
- }
- }
-
- OneSixVersion::VersionFile versionFile;
- versionFile.name = name;
- versionFile.id = fileId;
- versionFile.version = this->version;
- versionFile.mcVersion = mcVersion;
- versionFile.filename = filename;
- versionFile.order = order;
- version->versionFiles.append(versionFile);
-
- return NoApplyError;
- }
-};
-
OneSixVersionBuilder::OneSixVersionBuilder()
{
}
-bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance,
- QWidget *widgetParent, const bool onlyVanilla, const QStringList &external)
+void OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance,
+ const bool onlyVanilla, const QStringList &external)
{
OneSixVersionBuilder builder;
builder.m_version = version;
builder.m_instance = instance;
- builder.m_widgetParent = widgetParent;
- return builder.build(onlyVanilla, external);
+ builder.buildInternal(onlyVanilla, external);
}
-bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
+void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
+ const QJsonObject &obj)
{
OneSixVersionBuilder builder;
builder.m_version = version;
builder.m_instance = 0;
- builder.m_widgetParent = 0;
- return builder.read(obj);
+ builder.readJsonAndApply(obj);
}
-bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &external)
+void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
{
m_version->clear();
@@ -833,245 +64,194 @@ bool OneSixVersionBuilder::build(const bool onlyVanilla, const QStringList &exte
QDir patches(root.absoluteFilePath("patches/"));
// if we do external files, do just those.
- if(!external.isEmpty()) for (auto fileName : external)
- {
- QLOG_INFO() << "Reading" << fileName;
- VersionFile file;
- ParseFlags flags = fileName.endsWith("pack.json") ? IsFTBPackJson : NoFlags;
- if (!read(QFileInfo(fileName), false, &file, flags))
- {
- return false;
+ if (!external.isEmpty())
+ for (auto fileName : external)
+ {
+ QLOG_INFO() << "Reading" << fileName;
+ auto file =
+ parseJsonFile(QFileInfo(fileName), false, fileName.endsWith("pack.json"));
+ file->name = QFileInfo(fileName).fileName();
+ file->fileId = "org.multimc.external." + file->name;
+ file->version = QString();
+ file->mcVersion = QString();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
}
- file.name = QFileInfo(fileName).fileName();
- file.fileId = "org.multimc.external." + file.name;
- file.version = QString();
- file.mcVersion = QString();
- bool isError = false;
- auto errorcode = file.applyTo(m_version);
- if(errorcode != VersionFile::NoApplyError)
- return false;
- }
// else, if there's custom json, we just do that.
else if (QFile::exists(root.absoluteFilePath("custom.json")))
{
QLOG_INFO() << "Reading custom.json";
- VersionFile file;
- if (!read(QFileInfo(root.absoluteFilePath("custom.json")), false, &file))
- {
- return false;
- }
- file.name = "custom.json";
- file.filename = "custom.json";
- file.fileId = "org.multimc.custom.json";
- file.version = QString();
- auto errorcode = file.applyTo(m_version);
- if(errorcode != VersionFile::NoApplyError)
- return false;
- // QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC"));
+ auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false);
+ file->name = "custom.json";
+ file->filename = "custom.json";
+ file->fileId = "org.multimc.custom.json";
+ file->version = QString();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("The version descriptors of this instance are not compatible with the
+ // current version of MultiMC"));
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more info.")
}
// version.json -> patches/*.json -> user.json
- else do
- {
- // version.json
- QLOG_INFO() << "Reading version.json";
- VersionFile file;
- if (!read(QFileInfo(root.absoluteFilePath("version.json")), false, &file))
- {
- return false;
- }
- file.name = "version.json";
- file.fileId = "org.multimc.version.json";
- file.version = m_instance->intendedVersionId();
- file.mcVersion = m_instance->intendedVersionId();
- auto error = file.applyTo(m_version);
- if (error != VersionFile::NoApplyError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr(
- "Error while applying %1. Please check MultiMC-0.log for more info.")
- .arg(root.absoluteFilePath("version.json")));
- return false;
- }
-
- if (onlyVanilla)
- break;
+ else
+ do
+ {
+ // version.json
+ QLOG_INFO() << "Reading version.json";
+ auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
+ file->name = "Minecraft";
+ file->fileId = "org.multimc.version.json";
+ file->version = m_instance->intendedVersionId();
+ file->mcVersion = m_instance->intendedVersionId();
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
+ // info.").arg(root.absoluteFilePath("version.json")));
+
+ if (onlyVanilla)
+ break;
- // patches/
- // load all, put into map for ordering, apply in the right order
- QMap<QString, int> overrideOrder = readOverrideOrders(m_instance);
+ // patches/
+ // load all, put into map for ordering, apply in the right order
- QMap<int, QPair<QString, VersionFile>> files;
- for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- QLOG_INFO() << "Reading" << info.fileName();
- VersionFile file;
- if (!read(info, true, &file))
- {
- return false;
- }
- if (overrideOrder.contains(file.fileId))
+ QMap<int, QPair<QString, VersionFilePtr>> files;
+ for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
{
- file.order = overrideOrder.value(file.fileId);
- }
- if (files.contains(file.order))
- {
- QLOG_ERROR() << file.fileId << "has the same order as" << files[file.order].second.fileId;
- return false;
+ QLOG_INFO() << "Reading" << info.fileName();
+ auto file = parseJsonFile(info, true);
+ if (files.contains(file->order))
+ {
+ throw VersionBuildError(QObject::tr("%1 has the same order as %2").arg(
+ file->fileId, files[file->order].second->fileId));
+ }
+ files.insert(file->order, qMakePair(info.fileName(), file));
}
- files.insert(file.order, qMakePair(info.fileName(), file));
- }
- for (auto order : files.keys())
- {
- QLOG_DEBUG() << "Applying file with order" << order;
- auto filePair = files[order];
- auto error = filePair.second.applyTo(m_version);
- if (error != VersionFile::NoApplyError)
+ for (auto order : files.keys())
{
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Error while applying %1. Please check MultiMC-0.log "
- "for more info.").arg(filePair.first));
- return false;
+ QLOG_DEBUG() << "Applying file with order" << order;
+ auto &filePair = files[order];
+ filePair.second->applyTo(m_version);
+ m_version->versionFiles.append(filePair.second);
}
- }
- } while(0);
+ } while (0);
// some final touches
+ finalizeVersion();
+}
+
+void OneSixVersionBuilder::finalizeVersion()
+{
+ if (m_version->assets.isEmpty())
{
- if (m_version->assets.isEmpty())
+ m_version->assets = "legacy";
+ }
+ if (m_version->minecraftArguments.isEmpty())
+ {
+ QString toCompare = m_version->processArguments.toLower();
+ if (toCompare == "legacy")
{
- m_version->assets = "legacy";
+ m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
- if (m_version->minecraftArguments.isEmpty())
+ else if (toCompare == "username_session")
{
- QString toCompare = m_version->processArguments.toLower();
- if (toCompare == "legacy")
- {
- m_version->minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- m_version->minecraftArguments =
- "--username ${auth_player_name} --session ${auth_session}";
- }
- else if (toCompare == "username_session_version")
- {
- m_version->minecraftArguments = "--username ${auth_player_name} "
- "--session ${auth_session} "
- "--version ${profile_name}";
- }
+ m_version->minecraftArguments =
+ "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ m_version->minecraftArguments = "--username ${auth_player_name} "
+ "--session ${auth_session} "
+ "--version ${profile_name}";
}
}
-
- return true;
}
-bool OneSixVersionBuilder::read(const QJsonObject &obj)
+void OneSixVersionBuilder::readJsonAndApply(const QJsonObject &obj)
{
m_version->clear();
- bool isError = false;
- VersionFile file = VersionFile::fromJson(QJsonDocument(obj), QString(), false, isError);
- if (isError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
- return false;
- }
- VersionFile::ApplyError error = file.applyTo(m_version);
- if (error == VersionFile::OtherError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
- return false;
- }
- else if (error == VersionFile::LauncherVersionError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("The version descriptors of this instance are not compatible with the current version of MultiMC"));
- return false;
- }
+ auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
+ // QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
- return true;
+ file->applyTo(m_version);
+ m_version->versionFiles.append(file);
+ // QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
+ // QObject::tr("The version descriptors of this instance are not compatible with the current
+ // version of MultiMC"));
}
-bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder,
- VersionFile *out, const ParseFlags flags)
+VersionFilePtr OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo,
+ const bool requireOrder, bool isFTB)
{
QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly))
{
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Unable to open %1: %2").arg(file.fileName(), file.errorString()));
- return false;
+ throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
+ .arg(fileInfo.fileName(), file.errorString()));
}
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError)
{
- QMessageBox::critical(m_widgetParent, QObject::tr("Error"),
- QObject::tr("Unable to parse %1: %2 at %3")
- .arg(file.fileName(), error.errorString())
- .arg(error.offset));
- return false;
+ throw JSONValidationError(QObject::tr("Unable to process the version file %1: %2 at %3.")
+ .arg(fileInfo.fileName(), error.errorString())
+ .arg(error.offset));
}
- bool isError = false;
- *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError, flags);
- if (isError)
- {
- QMessageBox::critical(
- m_widgetParent, QObject::tr("Error"),
- QObject::tr("Error while reading %1. Please check MultiMC-0.log for more info.")
- .arg(file.fileName()));
- ;
- }
- return true;
+ return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
+ // QObject::tr("Error while reading %1. Please check MultiMC-0.log for more
+ // info.").arg(file.fileName());
}
QMap<QString, int> OneSixVersionBuilder::readOverrideOrders(OneSixInstance *instance)
{
QMap<QString, int> out;
- if (QDir(instance->instanceRoot()).exists("order.json"))
+
+ // make sure the order file exists
+ if (!QDir(instance->instanceRoot()).exists("order.json"))
+ return out;
+
+ // and it can be opened
+ QFile orderFile(instance->instanceRoot() + "/order.json");
+ if (!orderFile.open(QFile::ReadOnly))
{
- QFile orderFile(instance->instanceRoot() + "/order.json");
- if (!orderFile.open(QFile::ReadOnly))
- {
- QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
- << " for reading:" << orderFile.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- }
- else
+ QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
+ << " for reading:" << orderFile.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
+
+ // and it's valid JSON
+ QJsonParseError error;
+ QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
+ if (error.error != QJsonParseError::NoError)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
+
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = MMCJson::ensureObject(doc);
+ for (auto it = obj.begin(); it != obj.end(); ++it)
{
- QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
- if (error.error != QJsonParseError::NoError || !doc.isObject())
- {
- QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ":"
- << error.errorString();
- QLOG_WARN() << "Ignoring overriden order";
- }
- else
+ if (it.key().startsWith("org.multimc."))
{
- QJsonObject obj = doc.object();
- for (auto it = obj.begin(); it != obj.end(); ++it)
- {
- if (it.key().startsWith("org.multimc."))
- {
- continue;
- }
- out.insert(it.key(), it.value().toDouble());
- }
+ continue;
}
+ out.insert(it.key(), MMCJson::ensureInteger(it.value()));
}
}
+ catch (JSONValidationError &err)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
+ QLOG_WARN() << "Ignoring overriden order";
+ return out;
+ }
return out;
}
+
bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
OneSixInstance *instance)
{
@@ -1094,4 +274,3 @@ bool OneSixVersionBuilder::writeOverrideOrders(const QMap<QString, int> &order,
orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
return true;
}
-
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
index 8cf6f32f..8be3d9d3 100644
--- a/logic/OneSixVersionBuilder.h
+++ b/logic/OneSixVersionBuilder.h
@@ -17,39 +17,32 @@
#include <QString>
#include <QMap>
+#include "VersionFile.h"
-class OneSixVersion;
+class VersionFinal;
class OneSixInstance;
-class QWidget;
class QJsonObject;
class QFileInfo;
-class VersionFile;
class OneSixVersionBuilder
{
OneSixVersionBuilder();
public:
- static bool build(OneSixVersion *version, OneSixInstance *instance, QWidget *widgetParent, const bool onlyVanilla, const QStringList &external);
- static bool read(OneSixVersion *version, const QJsonObject &obj);
+ static void build(VersionFinal *version, OneSixInstance *instance, const bool onlyVanilla,
+ const QStringList &external);
+ static void readJsonAndApplyToVersion(VersionFinal *version, const QJsonObject &obj);
+
static QMap<QString, int> readOverrideOrders(OneSixInstance *instance);
static bool writeOverrideOrders(const QMap<QString, int> &order, OneSixInstance *instance);
- enum ParseFlag
- {
- NoFlags = 0x0,
- IsFTBPackJson = 0x1
- };
- Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
-
private:
- OneSixVersion *m_version;
+ VersionFinal *m_version;
OneSixInstance *m_instance;
- QWidget *m_widgetParent;
- bool build(const bool onlyVanilla, const QStringList &external);
- bool read(const QJsonObject &obj);
+ void buildInternal(const bool onlyVanilla, const QStringList &external);
+ void readJsonAndApply(const QJsonObject &obj);
+ void finalizeVersion();
- bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out, const ParseFlags flags = NoFlags);
+ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
+ bool isFTB = false);
};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(OneSixVersionBuilder::ParseFlags)
diff --git a/logic/VersionFile.cpp b/logic/VersionFile.cpp
new file mode 100644
index 00000000..831b086e
--- /dev/null
+++ b/logic/VersionFile.cpp
@@ -0,0 +1,535 @@
+#include <QJsonArray>
+#include <QJsonDocument>
+
+#include <modutils.h>
+
+#include "logger/QsLog.h"
+#include "logic/VersionFile.h"
+#include "logic/OneSixLibrary.h"
+#include "logic/VersionFinal.h"
+#include "MMCJson.h"
+
+using namespace MMCJson;
+
+#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
+
+RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
+{
+ RawLibraryPtr out(new RawLibrary());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a library that doesn't have a 'name' field");
+ }
+ out->name = libObj.value("name").toString();
+
+ auto readString = [libObj, filename](const QString & key, QString & variable)
+ {
+ if (libObj.contains(key))
+ {
+ QJsonValue val = libObj.value(key);
+ if (!val.isString())
+ {
+ QLOG_WARN() << key << "is not a string in" << filename << "(skipping)";
+ }
+ else
+ {
+ variable = val.toString();
+ }
+ }
+ };
+
+ readString("url", out->url);
+ readString("MMC-hint", out->hint);
+ readString("MMC-absulute_url", out->absoluteUrl);
+ readString("MMC-absoluteUrl", out->absoluteUrl);
+ if (libObj.contains("extract"))
+ {
+ out->applyExcludes = true;
+ auto extractObj = ensureObject(libObj.value("extract"));
+ for (auto excludeVal : ensureArray(extractObj.value("exclude")))
+ {
+ out->excludes.append(ensureString(excludeVal));
+ }
+ }
+ if (libObj.contains("natives"))
+ {
+ out->applyNatives = true;
+ QJsonObject nativesObj = ensureObject(libObj.value("natives"));
+ for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ {
+ if (!it.value().isString())
+ {
+ QLOG_WARN() << filename << "contains an invalid native (skipping)";
+ }
+ OpSys opSys = OpSys_fromString(it.key());
+ if (opSys != Os_Other)
+ {
+ out->natives.append(qMakePair(opSys, it.value().toString()));
+ }
+ }
+ }
+ if (libObj.contains("rules"))
+ {
+ out->applyRules = true;
+ out->rules = rulesFromJsonV4(libObj);
+ }
+ return out;
+}
+
+VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB)
+{
+ VersionFilePtr out(new VersionFile());
+ if (doc.isEmpty() || doc.isNull())
+ {
+ throw JSONValidationError(filename + " is empty or null");
+ }
+ if (!doc.isObject())
+ {
+ throw JSONValidationError("The root of " + filename + " is not an object");
+ }
+
+ QJsonObject root = doc.object();
+
+ if (requireOrder)
+ {
+ if (root.contains("order"))
+ {
+ out->order = ensureInteger(root.value("order"));
+ }
+ else
+ {
+ // FIXME: evaluate if we don't want to throw exceptions here instead
+ QLOG_ERROR() << filename << "doesn't contain an order field";
+ }
+ }
+
+ out->name = root.value("name").toString();
+ out->fileId = root.value("fileId").toString();
+ out->version = root.value("version").toString();
+ out->mcVersion = root.value("mcVersion").toString();
+ out->filename = filename;
+
+ auto readString = [root, filename](const QString & key, QString & variable)
+ {
+ if (root.contains(key))
+ {
+ variable = ensureString(root.value(key));
+ }
+ };
+
+ // FIXME: This should be ignored when applying.
+ if (!isFTB)
+ {
+ readString("id", out->id);
+ }
+
+ readString("mainClass", out->mainClass);
+ readString("processArguments", out->processArguments);
+ readString("minecraftArguments", out->overwriteMinecraftArguments);
+ readString("+minecraftArguments", out->addMinecraftArguments);
+ readString("-minecraftArguments", out->removeMinecraftArguments);
+ readString("type", out->type);
+ readString("releaseTime", out->releaseTime);
+ readString("time", out->time);
+ readString("assets", out->assets);
+
+ if (root.contains("minimumLauncherVersion"))
+ {
+ out->minimumLauncherVersion = ensureInteger(root.value("minimumLauncherVersion"));
+ }
+
+ if (root.contains("tweakers"))
+ {
+ out->shouldOverwriteTweakers = true;
+ for (auto tweakerVal : ensureArray(root.value("tweakers")))
+ {
+ out->overwriteTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("+tweakers"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("+tweakers")))
+ {
+ out->addTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("-tweakers"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("-tweakers")))
+ {
+ out->removeTweakers.append(ensureString(tweakerVal));
+ }
+ }
+
+ if (root.contains("libraries"))
+ {
+ // FIXME: This should be done when applying.
+ out->shouldOverwriteLibs = !isFTB;
+ for (auto libVal : ensureArray(root.value("libraries")))
+ {
+ auto libObj = ensureObject(libVal);
+
+ auto lib = RawLibrary::fromJson(libObj, filename);
+ // FIXME: This should be done when applying.
+ if (isFTB)
+ {
+ lib->hint = "local";
+ lib->insertType = RawLibrary::Prepend;
+ out->addLibs.prepend(lib);
+ }
+ else
+ {
+ out->overwriteLibs.append(lib);
+ }
+ }
+ }
+
+ if (root.contains("+libraries"))
+ {
+ for (auto libVal : ensureArray(root.value("+libraries")))
+ {
+ QJsonObject libObj = ensureObject(libVal);
+ QJsonValue insertVal = ensureExists(libObj.value("insert"));
+
+ // parse the library
+ auto lib = RawLibrary::fromJson(libObj, filename);
+
+ // TODO: utility functions for handling this case. templates?
+ QString insertString;
+ {
+ if (insertVal.isString())
+ {
+ insertString = insertVal.toString();
+ }
+ else if (insertVal.isObject())
+ {
+ QJsonObject insertObj = insertVal.toObject();
+ if (insertObj.isEmpty())
+ {
+ throw JSONValidationError("One library has an empty insert object in " +
+ filename);
+ }
+ insertString = insertObj.keys().first();
+ lib->insertData = insertObj.value(insertString).toString();
+ }
+ }
+ if (insertString == "apply")
+ {
+ lib->insertType = RawLibrary::Apply;
+ }
+ else if (insertString == "prepend")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "append")
+ {
+ lib->insertType = RawLibrary::Prepend;
+ }
+ else if (insertString == "replace")
+ {
+ lib->insertType = RawLibrary::Replace;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid insert type");
+ }
+ if (libObj.contains("MMC-depend"))
+ {
+ const QString dependString = ensureString(libObj.value("MMC-depend"));
+ if (dependString == "hard")
+ {
+ lib->dependType = RawLibrary::Hard;
+ }
+ else if (dependString == "soft")
+ {
+ lib->dependType = RawLibrary::Soft;
+ }
+ else
+ {
+ throw JSONValidationError("A '+' library in " + filename +
+ " contains an invalid depend type");
+ }
+ }
+ out->addLibs.append(lib);
+ }
+ }
+ if (root.contains("-libraries"))
+ {
+ for (auto libVal : ensureArray(root.value("-libraries")))
+ {
+ auto libObj = ensureObject(libVal);
+ out->removeLibs.append(ensureString(libObj.value("name")));
+ }
+ }
+ return out;
+}
+
+OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
+{
+ std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name));
+ if (!lib->url.isEmpty())
+ {
+ out->setBaseUrl(lib->url);
+ }
+ out->setHint(lib->hint);
+ if (!lib->absoluteUrl.isEmpty())
+ {
+ out->setAbsoluteUrl(lib->absoluteUrl);
+ }
+ out->setAbsoluteUrl(lib->absoluteUrl);
+ out->extract_excludes = lib->excludes;
+ for (auto native : lib->natives)
+ {
+ out->addNative(native.first, native.second);
+ }
+ out->setRules(lib->rules);
+ out->finalize();
+ return out;
+}
+
+int VersionFile::findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
+{
+ for (int i = 0; i < haystack.size(); ++i)
+ {
+ if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix)
+ .indexIn(haystack.at(i)->rawName()) != -1)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void VersionFile::applyTo(VersionFinal *version)
+{
+ if (minimumLauncherVersion != -1)
+ {
+ if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
+ {
+ throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION);
+ }
+ }
+
+ if (!version->id.isNull() && !mcVersion.isNull())
+ {
+ if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) ==
+ -1)
+ {
+ throw MinecraftVersionMismatch(fileId, mcVersion, version->id);
+ }
+ }
+
+ if (!id.isNull())
+ {
+ version->id = id;
+ }
+ if (!mainClass.isNull())
+ {
+ version->mainClass = mainClass;
+ }
+ if (!processArguments.isNull())
+ {
+ version->processArguments = processArguments;
+ }
+ if (!type.isNull())
+ {
+ version->type = type;
+ }
+ if (!releaseTime.isNull())
+ {
+ version->releaseTime = releaseTime;
+ }
+ if (!time.isNull())
+ {
+ version->time = time;
+ }
+ if (!assets.isNull())
+ {
+ version->assets = assets;
+ }
+ if (minimumLauncherVersion >= 0)
+ {
+ version->minimumLauncherVersion = minimumLauncherVersion;
+ }
+ if (!overwriteMinecraftArguments.isNull())
+ {
+ version->minecraftArguments = overwriteMinecraftArguments;
+ }
+ if (!addMinecraftArguments.isNull())
+ {
+ version->minecraftArguments += addMinecraftArguments;
+ }
+ if (!removeMinecraftArguments.isNull())
+ {
+ version->minecraftArguments.remove(removeMinecraftArguments);
+ }
+ if (shouldOverwriteTweakers)
+ {
+ version->tweakers = overwriteTweakers;
+ }
+ for (auto tweaker : addTweakers)
+ {
+ version->tweakers += tweaker;
+ }
+ for (auto tweaker : removeTweakers)
+ {
+ version->tweakers.removeAll(tweaker);
+ }
+ if (shouldOverwriteLibs)
+ {
+ version->libraries.clear();
+ for (auto lib : overwriteLibs)
+ {
+ version->libraries.append(createLibrary(lib));
+ }
+ }
+ for (auto lib : addLibs)
+ {
+ switch (lib->insertType)
+ {
+ case RawLibrary::Apply:
+ {
+
+ int index = findLibrary(version->libraries, lib->name);
+ if (index >= 0)
+ {
+ auto library = version->libraries[index];
+ if (!lib->url.isNull())
+ {
+ library->setBaseUrl(lib->url);
+ }
+ if (!lib->hint.isNull())
+ {
+ library->setHint(lib->hint);
+ }
+ if (!lib->absoluteUrl.isNull())
+ {
+ library->setAbsoluteUrl(lib->absoluteUrl);
+ }
+ if (lib->applyExcludes)
+ {
+ library->extract_excludes = lib->excludes;
+ }
+ if (lib->applyNatives)
+ {
+ library->clearSuffixes();
+ for (auto native : lib->natives)
+ {
+ library->addNative(native.first, native.second);
+ }
+ }
+ if (lib->applyRules)
+ {
+ library->setRules(lib->rules);
+ }
+ library->finalize();
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib->name << "(skipping)";
+ }
+ break;
+ }
+ case RawLibrary::Append:
+ case RawLibrary::Prepend:
+ {
+
+ const int startOfVersion = lib->name.lastIndexOf(':') + 1;
+ const int index = findLibrary(
+ version->libraries, QString(lib->name).replace(startOfVersion, INT_MAX, '*'));
+ if (index < 0)
+ {
+ if (lib->insertType == RawLibrary::Append)
+ {
+ version->libraries.append(createLibrary(lib));
+ }
+ else
+ {
+ version->libraries.prepend(createLibrary(lib));
+ }
+ }
+ else
+ {
+ auto otherLib = version->libraries.at(index);
+ const Util::Version ourVersion = lib->name.mid(startOfVersion, INT_MAX);
+ const Util::Version otherVersion = otherLib->version();
+ // if the existing version is a hard dependency we can either use it or
+ // fail, but we can't change it
+ if (otherLib->dependType == OneSixLibrary::Hard)
+ {
+ // we need a higher version, or we're hard to and the versions aren't
+ // equal
+ if (ourVersion > otherVersion ||
+ (lib->dependType == RawLibrary::Hard && ourVersion != otherVersion))
+ {
+ throw VersionBuildError(
+ QObject::tr(
+ "Error resolving library dependencies between %1 and %2 in %3.")
+ .arg(otherLib->rawName(), lib->name, filename));
+ }
+ else
+ {
+ // the library is already existing, so we don't have to do anything
+ }
+ }
+ else if (otherLib->dependType == OneSixLibrary::Soft)
+ {
+ // if we are higher it means we should update
+ if (ourVersion > otherVersion)
+ {
+ auto library = createLibrary(lib);
+ if (Util::Version(otherLib->minVersion) < ourVersion)
+ {
+ library->minVersion = ourVersion.toString();
+ }
+ version->libraries.replace(index, library);
+ }
+ else
+ {
+ // our version is smaller than the existing version, but we require
+ // it: fail
+ if (lib->dependType == RawLibrary::Hard)
+ {
+ throw VersionBuildError(QObject::tr(
+ "Error resolving library dependencies between %1 and %2 in %3.")
+ .arg(otherLib->rawName(), lib->name,
+ filename));
+ }
+ }
+ }
+ }
+ break;
+ }
+ case RawLibrary::Replace:
+ {
+ int index = findLibrary(version->libraries, lib->insertData);
+ if (index >= 0)
+ {
+ version->libraries.replace(index, createLibrary(lib));
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib->insertData << "(skipping)";
+ }
+ break;
+ }
+ }
+ }
+ for (auto lib : removeLibs)
+ {
+ int index = findLibrary(version->libraries, lib);
+ if (index >= 0)
+ {
+ version->libraries.removeAt(index);
+ }
+ else
+ {
+ QLOG_WARN() << "Couldn't find" << lib << "(skipping)";
+ }
+ }
+}
diff --git a/logic/VersionFile.h b/logic/VersionFile.h
new file mode 100644
index 00000000..169a2066
--- /dev/null
+++ b/logic/VersionFile.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <QString>
+#include <QStringList>
+#include <memory>
+#include "logic/OpSys.h"
+#include "logic/OneSixRule.h"
+#include "MMCError.h"
+
+class VersionFinal;
+
+class VersionBuildError : public MMCError
+{
+public:
+ VersionBuildError(QString cause) : MMCError(cause) {};
+ virtual ~VersionBuildError() noexcept {}
+};
+
+/**
+ * the base version file was meant for a newer version of the vanilla launcher than we support
+ */
+class LauncherVersionError : public VersionBuildError
+{
+public:
+ LauncherVersionError(int actual, int supported)
+ : VersionBuildError(QObject::tr(
+ "The base version file of this instance was meant for a newer (%1) "
+ "version of the vanilla launcher than this version of MultiMC supports (%2).")
+ .arg(actual)
+ .arg(supported)) {};
+ virtual ~LauncherVersionError() noexcept {}
+};
+
+/**
+ * some patch was intended for a different version of minecraft
+ */
+class MinecraftVersionMismatch : public VersionBuildError
+{
+public:
+ MinecraftVersionMismatch(QString fileId, QString mcVersion, QString parentMcVersion)
+ : VersionBuildError(QObject::tr("The patch %1 is for a different version of Minecraft "
+ "(%2) than that of the instance (%3).")
+ .arg(fileId)
+ .arg(mcVersion)
+ .arg(parentMcVersion)) {};
+ virtual ~MinecraftVersionMismatch() noexcept {}
+};
+
+struct RawLibrary;
+typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
+struct RawLibrary
+{
+ QString name;
+ QString url;
+ QString hint;
+ QString absoluteUrl;
+ bool applyExcludes = false;
+ QStringList excludes;
+ bool applyNatives = false;
+ QList<QPair<OpSys, QString>> natives;
+ bool applyRules = false;
+ QList<std::shared_ptr<Rule>> rules;
+
+ // user for '+' libraries
+ enum InsertType
+ {
+ Apply,
+ Append,
+ Prepend,
+ Replace
+ };
+ InsertType insertType = Append;
+ QString insertData;
+ enum DependType
+ {
+ Soft,
+ Hard
+ };
+ DependType dependType = Soft;
+
+ static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
+};
+
+struct VersionFile;
+typedef std::shared_ptr<VersionFile> VersionFilePtr;
+struct VersionFile
+{
+public: /* methods */
+ static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB = false);
+
+ static OneSixLibraryPtr createLibrary(RawLibraryPtr lib);
+ int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle);
+ void applyTo(VersionFinal *version);
+
+public: /* data */
+ int order = 0;
+ QString name;
+ QString fileId;
+ QString version;
+ // TODO use the mcVersion to determine if a version file should be removed on update
+ QString mcVersion;
+ QString filename;
+ // TODO requirements
+ // QMap<QString, QString> requirements;
+ QString id;
+ QString mainClass;
+ QString overwriteMinecraftArguments;
+ QString addMinecraftArguments;
+ QString removeMinecraftArguments;
+ QString processArguments;
+ QString type;
+ QString releaseTime;
+ QString time;
+ QString assets;
+ int minimumLauncherVersion = -1;
+
+ bool shouldOverwriteTweakers = false;
+ QStringList overwriteTweakers;
+ QStringList addTweakers;
+ QStringList removeTweakers;
+
+ bool shouldOverwriteLibs = false;
+ QList<RawLibraryPtr> overwriteLibs;
+ QList<RawLibraryPtr> addLibs;
+ QList<QString> removeLibs;
+};
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
new file mode 100644
index 00000000..a057ecdd
--- /dev/null
+++ b/logic/VersionFinal.cpp
@@ -0,0 +1,183 @@
+/* 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 "VersionFinal.h"
+
+#include <QDebug>
+#include <QFile>
+
+#include "OneSixVersionBuilder.h"
+
+VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
+ : QAbstractListModel(parent), m_instance(instance)
+{
+ clear();
+}
+
+bool VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
+{
+ //FIXME: source of epic failure.
+ beginResetModel();
+ OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
+ endResetModel();
+}
+
+void VersionFinal::clear()
+{
+ beginResetModel();
+ id.clear();
+ time.clear();
+ releaseTime.clear();
+ type.clear();
+ assets.clear();
+ processArguments.clear();
+ minecraftArguments.clear();
+ minimumLauncherVersion = 0xDEADBEAF;
+ mainClass.clear();
+ libraries.clear();
+ tweakers.clear();
+ versionFiles.clear();
+ endResetModel();
+}
+
+bool VersionFinal::canRemove(const int index) const
+{
+ if (index < versionFiles.size())
+ {
+ return versionFiles.at(index)->fileId != "org.multimc.version.json";
+ }
+ return false;
+}
+
+QString VersionFinal::versionFileId(const int index) const
+{
+ if (index < 0 || index >= versionFiles.size())
+ {
+ return QString();
+ }
+ return versionFiles.at(index)->fileId;
+}
+
+bool VersionFinal::remove(const int index)
+{
+ if (canRemove(index))
+ {
+ return QFile::remove(versionFiles.at(index)->filename);
+ }
+ return false;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && !lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+std::shared_ptr<VersionFinal> VersionFinal::fromJson(const QJsonObject &obj)
+{
+ std::shared_ptr<VersionFinal> version(new VersionFinal(0));
+ try
+ {
+ OneSixVersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
+ }
+ catch(MMCError & err)
+ {
+ return 0;
+ }
+ return version;
+}
+
+QVariant VersionFinal::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= versionFiles.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ {
+ switch (column)
+ {
+ case 0:
+ return versionFiles.at(row)->name;
+ case 1:
+ return versionFiles.at(row)->version;
+ default:
+ return QVariant();
+ }
+ }
+ return QVariant();
+}
+
+QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal)
+ {
+ if (role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case 0:
+ return tr("Name");
+ case 1:
+ return tr("Version");
+ default:
+ return QVariant();
+ }
+ }
+ }
+ return QVariant();
+}
+
+Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+int VersionFinal::rowCount(const QModelIndex &parent) const
+{
+ return versionFiles.size();
+}
+
+int VersionFinal::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
diff --git a/logic/OneSixVersion.h b/logic/VersionFinal.h
index fee47fa3..99fd5ff0 100644
--- a/logic/OneSixVersion.h
+++ b/logic/VersionFinal.h
@@ -22,14 +22,15 @@
#include <memory>
#include "OneSixLibrary.h"
+#include "VersionFile.h"
class OneSixInstance;
-class OneSixVersion : public QAbstractListModel
+class VersionFinal : public QAbstractListModel
{
Q_OBJECT
public:
- explicit OneSixVersion(OneSixInstance *instance, QObject *parent = 0);
+ explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
@@ -37,7 +38,7 @@ public:
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- bool reload(QWidget *widgetParent, const bool onlyVanilla = false, const QStringList &external = QStringList());
+ bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
void clear();
void dump() const;
@@ -54,7 +55,7 @@ public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
- static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
+ static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
// data members
public:
@@ -118,20 +119,8 @@ public:
*/
// QList<Rule> rules;
- struct VersionFile
- {
- QString name;
- QString id;
- QString version;
- QString mcVersion;
- QString filename;
- int order;
- };
- QList<VersionFile> versionFiles;
+ QList<VersionFilePtr> versionFiles;
private:
OneSixInstance *m_instance;
};
-
-QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
-QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);
diff --git a/logic/lists/LiteLoaderVersionList.cpp b/logic/lists/LiteLoaderVersionList.cpp
index b8cea442..ef95eefd 100644
--- a/logic/lists/LiteLoaderVersionList.cpp
+++ b/logic/lists/LiteLoaderVersionList.cpp
@@ -92,7 +92,6 @@ void LiteLoaderVersionList::updateListData(QList<BaseVersionPtr> versions)
LLListLoadTask::LLListLoadTask(LiteLoaderVersionList *vlist)
{
m_list = vlist;
- vlistReply = nullptr;
}
LLListLoadTask::~LLListLoadTask()
@@ -102,23 +101,49 @@ LLListLoadTask::~LLListLoadTask()
void LLListLoadTask::executeTask()
{
setStatus(tr("Loading LiteLoader version list..."));
- auto worker = MMC->qnam();
- vlistReply = worker->get(QNetworkRequest(QUrl(URLConstants::LITELOADER_URL)));
- connect(vlistReply, SIGNAL(finished()), this, SLOT(listDownloaded()));
+ auto job = new NetJob("Version index");
+ // we do not care if the version is stale or not.
+ auto liteloaderEntry = MMC->metacache()->resolveEntry("liteloader", "versions.json");
+
+ // verify by poking the server.
+ liteloaderEntry->stale = true;
+
+ job->addNetAction(listDownload = CacheDownload::make(QUrl(URLConstants::LITELOADER_URL),
+ liteloaderEntry));
+
+ connect(listDownload.get(), SIGNAL(failed(int)), SLOT(listFailed()));
+
+ listJob.reset(job);
+ connect(listJob.get(), SIGNAL(succeeded()), SLOT(listDownloaded()));
+ connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ listJob->start();
+}
+
+void LLListLoadTask::listFailed()
+{
+ emitFailed("Failed to load LiteLoader version list.");
+ return;
}
void LLListLoadTask::listDownloaded()
{
- if (vlistReply->error() != QNetworkReply::NoError)
+ QByteArray data;
{
- vlistReply->deleteLater();
- emitFailed("Failed to load LiteLoader version list" + vlistReply->errorString());
- return;
+ auto dlJob = listDownload;
+ auto filename = std::dynamic_pointer_cast<CacheDownload>(dlJob)->getTargetFilepath();
+ QFile listFile(filename);
+ if (!listFile.open(QIODevice::ReadOnly))
+ {
+ emitFailed("Failed to open the LiteLoader version list.");
+ return;
+ }
+ data = listFile.readAll();
+ listFile.close();
+ dlJob.reset();
}
QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
- vlistReply->deleteLater();
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
{
@@ -140,7 +165,12 @@ void LLListLoadTask::listDownloaded()
emitFailed("Error parsing version list JSON: missing 'versions' object");
return;
}
- const QJsonObject versions = root.value("versions").toObject();
+
+ auto meta = root.value("meta").toObject();
+ QString description = meta.value("description").toString(tr("This is a lightweight loader for mods that don't change game mechanics."));
+ QString defaultUrl = meta.value("url").toString("http://dl.liteloader.com");
+ QString authors = meta.value("authors").toString("Mumfrey");
+ auto versions = root.value("versions").toObject();
QList<BaseVersionPtr> tempList;
for (auto vIt = versions.begin(); vIt != versions.end(); ++vIt)
@@ -170,6 +200,9 @@ void LLListLoadTask::listDownloaded()
version->md5 = artefact.value("md5").toString();
version->timestamp = artefact.value("timestamp").toDouble();
version->tweakClass = artefact.value("tweakClass").toString();
+ version->authors = authors;
+ version->description = description;
+ version->defaultUrl = defaultUrl;
const QJsonArray libs = artefact.value("libraries").toArray();
for (auto lIt = libs.begin(); lIt != libs.end(); ++lIt)
{
diff --git a/logic/lists/LiteLoaderVersionList.h b/logic/lists/LiteLoaderVersionList.h
index 8f761caf..bfc913e5 100644
--- a/logic/lists/LiteLoaderVersionList.h
+++ b/logic/lists/LiteLoaderVersionList.h
@@ -22,6 +22,7 @@
#include "BaseVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/BaseVersion.h"
+#include <logic/net/NetJob.h>
class LLListLoadTask;
class QNetworkReply;
@@ -46,6 +47,7 @@ public:
return version;
}
+ // important info
QString version;
QString file;
QString mcVersion;
@@ -54,6 +56,11 @@ public:
bool isLatest;
QString tweakClass;
QStringList libraries;
+
+ // meta
+ QString defaultUrl;
+ QString description;
+ QString authors;
};
typedef std::shared_ptr<LiteLoaderVersion> LiteLoaderVersionPtr;
@@ -96,8 +103,10 @@ public:
protected
slots:
void listDownloaded();
+ void listFailed();
protected:
- QNetworkReply *vlistReply;
+ NetJobPtr listJob;
+ CacheDownloadPtr listDownload;
LiteLoaderVersionList *m_list;
};
diff --git a/logic/net/PasteUpload.cpp b/logic/net/PasteUpload.cpp
index fa54d084..402eb400 100644
--- a/logic/net/PasteUpload.cpp
+++ b/logic/net/PasteUpload.cpp
@@ -25,7 +25,7 @@ void PasteUpload::executeTask()
m_reply = std::shared_ptr<QNetworkReply>(rep);
connect(rep, &QNetworkReply::downloadProgress, [&](qint64 value, qint64 max)
- { setProgress(value / max * 100); });
+ { setProgress(value / qMax((qint64)1, max) * 100); });
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(downloadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
@@ -52,10 +52,9 @@ void PasteUpload::downloadFinished()
emitFailed(jsonError.errorString());
return;
}
- QString error;
- if (!parseResult(doc, &error))
+ if (!parseResult(doc))
{
- emitFailed(error);
+ emitFailed(tr("paste.ee returned an error. Please consult the logs for more information"));
return;
}
}
@@ -69,13 +68,13 @@ void PasteUpload::downloadFinished()
emitSucceeded();
}
-bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError)
+bool PasteUpload::parseResult(QJsonDocument doc)
{
auto object = doc.object();
auto status = object.value("status").toString("error");
if (status == "error")
{
- parseError = new QString(object.value("error").toString());
+ QLOG_ERROR() << "paste.ee reported error:" << QString(object.value("error").toString());
return false;
}
// FIXME: not the place for GUI things.
diff --git a/logic/net/PasteUpload.h b/logic/net/PasteUpload.h
index 917a0016..83876c17 100644
--- a/logic/net/PasteUpload.h
+++ b/logic/net/PasteUpload.h
@@ -14,7 +14,7 @@ protected:
virtual void executeTask();
private:
- bool parseResult(QJsonDocument doc, QString *parseError);
+ bool parseResult(QJsonDocument doc);
QString m_text;
QString m_error;
QWidget *m_window;