summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp149
-rw-r--r--gui/dialogs/OneSixModEditDialog.h7
-rw-r--r--gui/dialogs/OneSixModEditDialog.ui53
-rw-r--r--logic/BaseInstaller.cpp66
-rw-r--r--logic/BaseInstaller.h39
-rw-r--r--logic/ForgeInstaller.cpp91
-rw-r--r--logic/ForgeInstaller.h10
-rw-r--r--logic/InstanceFactory.cpp10
-rw-r--r--logic/LiteLoaderInstaller.cpp100
-rw-r--r--logic/LiteLoaderInstaller.h20
-rw-r--r--logic/OneSixFTBInstance.cpp6
-rw-r--r--logic/OneSixInstance.cpp118
-rw-r--r--logic/OneSixInstance.h27
-rw-r--r--logic/OneSixInstance_p.h12
-rw-r--r--logic/OneSixLibrary.cpp36
-rw-r--r--logic/OneSixLibrary.h32
-rw-r--r--logic/OneSixRule.cpp4
-rw-r--r--logic/OneSixRule.h2
-rw-r--r--logic/OneSixUpdate.cpp4
-rw-r--r--logic/OneSixVersion.cpp352
-rw-r--r--logic/OneSixVersion.h60
-rw-r--r--logic/OneSixVersionBuilder.cpp919
-rw-r--r--logic/OneSixVersionBuilder.h43
24 files changed, 1581 insertions, 591 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c34f363f..ba28a042 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -411,10 +411,7 @@ logic/LegacyUpdate.cpp
logic/LegacyForge.h
logic/LegacyForge.cpp
-# 1.6 instances
-logic/OneSixInstance.h
-logic/OneSixInstance.cpp
-logic/OneSixInstance_p.h
+# OneSix instances
logic/OneSixUpdate.h
logic/OneSixUpdate.cpp
logic/OneSixVersion.h
@@ -425,10 +422,17 @@ logic/OneSixRule.h
logic/OneSixRule.cpp
logic/OpSys.h
logic/OpSys.cpp
+logic/BaseInstaller.h
+logic/BaseInstaller.cpp
logic/ForgeInstaller.h
logic/ForgeInstaller.cpp
logic/LiteLoaderInstaller.h
logic/LiteLoaderInstaller.cpp
+logic/OneSixInstance.h
+logic/OneSixInstance.cpp
+logic/OneSixInstance_p.h
+logic/OneSixVersionBuilder.h
+logic/OneSixVersionBuilder.cpp
# Nostalgia
logic/NostalgiaInstance.h
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index 27315c69..1742ff80 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -55,7 +55,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
main_model->setSourceModel(m_version.get());
ui->libraryTreeView->setModel(main_model);
ui->libraryTreeView->installEventFilter(this);
- ui->mainClassEdit->setText(m_version->mainClass);
+ connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
+ this, &OneSixModEditDialog::versionCurrent);
updateVersionControls();
}
else
@@ -81,6 +82,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
ui->resPackTreeView->installEventFilter(this);
m_resourcepacks->startWatching();
}
+
+ connect(m_inst, &OneSixInstance::versionReloaded, this, &OneSixModEditDialog::updateVersionControls);
}
OneSixModEditDialog::~OneSixModEditDialog()
@@ -92,98 +95,76 @@ OneSixModEditDialog::~OneSixModEditDialog()
void OneSixModEditDialog::updateVersionControls()
{
- bool customVersion = m_inst->versionIsCustom();
- ui->customizeBtn->setEnabled(!customVersion);
- ui->revertBtn->setEnabled(customVersion);
ui->forgeBtn->setEnabled(true);
- ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply());
- ui->customEditorBtn->setEnabled(customVersion);
+ ui->liteloaderBtn->setEnabled(LiteLoaderInstaller().canApply(m_inst));
+ ui->mainClassEdit->setText(m_version->mainClass);
}
void OneSixModEditDialog::disableVersionControls()
{
- ui->customizeBtn->setEnabled(false);
- ui->revertBtn->setEnabled(false);
ui->forgeBtn->setEnabled(false);
ui->liteloaderBtn->setEnabled(false);
- ui->customEditorBtn->setEnabled(false);
+ ui->reloadLibrariesBtn->setEnabled(false);
+ ui->removeLibraryBtn->setEnabled(false);
+ ui->mainClassEdit->setText("");
}
-void OneSixModEditDialog::on_customizeBtn_clicked()
+void OneSixModEditDialog::on_userEditorBtn_clicked()
{
- if (m_inst->customizeVersion())
+ QDir root(m_inst->instanceRoot());
+ if (!root.exists("user.json"))
{
- m_version = m_inst->getFullVersion();
- main_model->setSourceModel(m_version.get());
- updateVersionControls();
+ QFile file(root.absoluteFilePath("user.json"));
+ if (!file.open(QFile::WriteOnly))
+ {
+ QMessageBox::critical(this, tr("Error"), tr("Couldn't write a skeletion user.json file: %1").arg(file.errorString()));
+ return;
+ }
+ file.write("{\n}");
+ file.close();
+ }
+ if (!MMC->openJsonEditor(root.absoluteFilePath("user.json")))
+ {
+ QMessageBox::warning(this, tr("Error"), tr("Unable to open user.json, check the settings"));
}
}
-void OneSixModEditDialog::on_revertBtn_clicked()
+void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
{
- auto response = CustomMessageBox::selectable(
- this, tr("Revert?"), tr("Do you want to revert the "
- "version of this instance to its original configuration?"),
- QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
- if (response == QMessageBox::Yes)
- {
- if (m_inst->revertCustomVersion())
- {
- m_version = m_inst->getFullVersion();
- main_model->setSourceModel(m_version.get());
- updateVersionControls();
- }
- }
+ m_inst->reloadVersion(this);
}
-void OneSixModEditDialog::on_customEditorBtn_clicked()
+void OneSixModEditDialog::on_removeLibraryBtn_clicked()
{
- if (m_inst->versionIsCustom())
+ if (ui->libraryTreeView->currentIndex().isValid())
{
- if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json"))
+ if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
+ {
+ QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
+ }
+ else
{
- QMessageBox::warning(this, tr("Error"),
- tr("Unable to open custom.json, check the settings"));
+ m_inst->reloadVersion(this);
}
}
}
void OneSixModEditDialog::on_forgeBtn_clicked()
{
+ if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+ {
+ if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
+ {
+ return;
+ }
+ QDir(m_inst->instanceRoot()).remove("custom.json");
+ }
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setFilter(1, m_inst->currentVersionId());
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
- if (m_inst->versionIsCustom())
- {
- auto reply = QMessageBox::question(
- this, tr("Revert?"),
- tr("This will revert any "
- "changes you did to the version up to this point. Is that "
- "OK?"),
- QMessageBox::Yes | QMessageBox::No);
- if (reply == QMessageBox::Yes)
- {
- m_inst->revertCustomVersion();
- m_inst->customizeVersion();
- {
- m_version = m_inst->getFullVersion();
- main_model->setSourceModel(m_version.get());
- updateVersionControls();
- }
- }
- else
- return;
- }
- else
- {
- m_inst->customizeVersion();
- m_version = m_inst->getFullVersion();
- main_model->setSourceModel(m_version.get());
- updateVersionControls();
- }
ForgeVersionPtr forgeVersion =
std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
if (!forgeVersion)
@@ -200,9 +181,9 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
// install
QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!forge.apply(m_version))
+ if (!forge.add(m_inst))
{
- // failure notice
+ QLOG_ERROR() << "Failure installing forge";
}
}
else
@@ -215,18 +196,27 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
// install
QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!forge.apply(m_version))
+ if (!forge.add(m_inst))
{
- // failure notice
+ QLOG_ERROR() << "Failure installing forge";
}
}
}
+ m_inst->reloadVersion(this);
}
void OneSixModEditDialog::on_liteloaderBtn_clicked()
{
- LiteLoaderInstaller liteloader(m_inst->intendedVersionId());
- if (!liteloader.canApply())
+ if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+ {
+ if (QMessageBox::question(this, tr("Revert?"), tr("This action will remove your custom.json. Continue?")) != QMessageBox::Yes)
+ {
+ return;
+ }
+ QDir(m_inst->instanceRoot()).remove("custom.json");
+ }
+ LiteLoaderInstaller liteloader;
+ if (!liteloader.canApply(m_inst))
{
QMessageBox::critical(
this, tr("LiteLoader"),
@@ -234,19 +224,16 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
"into this version of Minecraft"));
return;
}
- if (!m_inst->versionIsCustom())
- {
- m_inst->customizeVersion();
- m_version = m_inst->getFullVersion();
- main_model->setSourceModel(m_version.get());
- updateVersionControls();
- }
- if (!liteloader.apply(m_version))
+ if (!liteloader.add(m_inst))
{
QMessageBox::critical(this, tr("LiteLoader"),
tr("For reasons unknown, the LiteLoader installation failed. "
"Check your MultiMC log files for details."));
}
+ else
+ {
+ m_inst->reloadVersion(this);
+ }
}
bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent)
@@ -365,3 +352,15 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
Mod &m = m_mods->operator[](row);
ui->frame->updateWithMod(m);
}
+
+void OneSixModEditDialog::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (!current.isValid())
+ {
+ ui->removeLibraryBtn->setDisabled(true);
+ }
+ else
+ {
+ ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row()));
+ }
+}
diff --git a/gui/dialogs/OneSixModEditDialog.h b/gui/dialogs/OneSixModEditDialog.h
index 2510c59c..7e759f48 100644
--- a/gui/dialogs/OneSixModEditDialog.h
+++ b/gui/dialogs/OneSixModEditDialog.h
@@ -45,9 +45,9 @@ slots:
void on_buttonBox_rejected();
void on_forgeBtn_clicked();
void on_liteloaderBtn_clicked();
- void on_customizeBtn_clicked();
- void on_revertBtn_clicked();
- void on_customEditorBtn_clicked();
+ void on_userEditorBtn_clicked();
+ void on_reloadLibrariesBtn_clicked();
+ void on_removeLibraryBtn_clicked();
void updateVersionControls();
void disableVersionControls();
@@ -66,4 +66,5 @@ private:
public
slots:
void loaderCurrent(QModelIndex current, QModelIndex previous);
+ void versionCurrent(const QModelIndex &current, const QModelIndex &previous);
};
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
index 899e0cbf..d90c6d93 100644
--- a/gui/dialogs/OneSixModEditDialog.ui
+++ b/gui/dialogs/OneSixModEditDialog.ui
@@ -26,7 +26,7 @@
</size>
</property>
<property name="currentIndex">
- <number>1</number>
+ <number>0</number>
</property>
<widget class="QWidget" name="libTab">
<attribute name="title">
@@ -43,6 +43,9 @@
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
+ <attribute name="headerVisible">
+ <bool>true</bool>
+ </attribute>
</widget>
</item>
<item>
@@ -85,61 +88,23 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="customizeBtn">
- <property name="toolTip">
- <string>Create an customized copy of the base version</string>
- </property>
- <property name="text">
- <string>Customize</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="revertBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="toolTip">
- <string>Revert to original base version</string>
- </property>
- <property name="text">
- <string>Revert</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="Line" name="line">
- <property name="frameShadow">
- <enum>QFrame::Sunken</enum>
- </property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
- <widget class="QPushButton" name="addLibraryBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="toolTip">
- <string>Add new libraries</string>
- </property>
+ <widget class="QPushButton" name="reloadLibrariesBtn">
<property name="text">
- <string>&amp;Add</string>
+ <string>Reload</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeLibraryBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="toolTip">
- <string>Remove selected libraries</string>
- </property>
<property name="text">
- <string>&amp;Remove</string>
+ <string>Remove</string>
</property>
</widget>
</item>
@@ -151,9 +116,9 @@
</widget>
</item>
<item>
- <widget class="QPushButton" name="customEditorBtn">
+ <widget class="QPushButton" name="userEditorBtn">
<property name="text">
- <string>Open custom.json</string>
+ <string>Open user.json</string>
</property>
</widget>
</item>
diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp
new file mode 100644
index 00000000..92aa0c92
--- /dev/null
+++ b/logic/BaseInstaller.cpp
@@ -0,0 +1,66 @@
+/* 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 "BaseInstaller.h"
+
+#include <QFile>
+
+#include "OneSixVersion.h"
+#include "OneSixLibrary.h"
+#include "OneSixInstance.h"
+
+#include "cmdutils.h"
+
+BaseInstaller::BaseInstaller()
+{
+
+}
+
+bool BaseInstaller::isApplied(OneSixInstance *on)
+{
+ return QFile::exists(filename(on->instanceRoot()));
+}
+
+bool BaseInstaller::add(OneSixInstance *to)
+{
+ if (!patchesDir(to->instanceRoot()).exists())
+ {
+ QDir(to->instanceRoot()).mkdir("patches");
+ }
+
+ if (isApplied(to))
+ {
+ if (!remove(to))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool BaseInstaller::remove(OneSixInstance *from)
+{
+ return QFile::remove(filename(from->instanceRoot()));
+}
+
+QString BaseInstaller::filename(const QString &root) const
+{
+ return patchesDir(root).absoluteFilePath(id() + ".json");
+}
+QDir BaseInstaller::patchesDir(const QString &root) const
+{
+ return QDir(root + "/patches/");
+}
diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h
new file mode 100644
index 00000000..df7eab89
--- /dev/null
+++ b/logic/BaseInstaller.h
@@ -0,0 +1,39 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+class OneSixInstance;
+class QDir;
+class QString;
+
+class BaseInstaller
+{
+public:
+ BaseInstaller();
+
+ virtual bool canApply(OneSixInstance *instance) const { return true; }
+ bool isApplied(OneSixInstance *on);
+
+ virtual bool add(OneSixInstance *to);
+ virtual bool remove(OneSixInstance *from);
+
+protected:
+ virtual QString id() const = 0;
+ QString filename(const QString &root) const;
+ QDir patchesDir(const QString &root) const;
+};
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
index 8d4c5b41..47c42694 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/ForgeInstaller.cpp
@@ -21,7 +21,15 @@
#include <quazipfile.h>
#include <pathutils.h>
#include <QStringList>
+#include <QRegularExpression>
+#include <QRegularExpressionMatch>
#include "MultiMC.h"
+#include "OneSixInstance.h"
+
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QSaveFile>
+#include <QCryptographicHash>
ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
{
@@ -66,6 +74,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
QJsonObject installObj = installVal.toObject();
QString libraryName = installObj.value("path").toString();
internalPath = installObj.value("filePath").toString();
+ m_forgeVersionString = installObj.value("version").toString().remove("Forge").trimmed();
// where do we put the library? decode the mojang path
OneSixLibrary lib(libraryName);
@@ -103,13 +112,22 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
}
-bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
+bool ForgeInstaller::add(OneSixInstance *to)
{
+ if (!BaseInstaller::add(to))
+ {
+ return false;
+ }
+
+ QJsonObject obj;
+ obj.insert("order", 5);
+
if (!m_forge_version)
return false;
- to->externalUpdateStart();
int sliding_insert_window = 0;
{
+ QJsonArray librariesPlus;
+
// for each library in the version we are adding (except for the blacklisted)
QSet<QString> blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"};
for (auto lib : m_forge_version->libraries)
@@ -128,28 +146,79 @@ bool ForgeInstaller::apply(std::shared_ptr<OneSixVersion> to)
if (blacklist.contains(libName))
continue;
- // find an entry that matches this one
+ QJsonObject libObj = lib->toJson();
+
bool found = false;
- for (auto tolib : to->libraries)
+ bool equals = false;
+ // find an entry that matches this one
+ for (auto tolib : to->getNonCustomVersion()->libraries)
{
if (tolib->name() != libName)
continue;
found = true;
+ if (tolib->toJson() == libObj)
+ {
+ equals = true;
+ }
// replace lib
- tolib = lib;
+ libObj.insert("insert", QString("replace"));
break;
}
+ if (equals)
+ {
+ continue;
+ }
if (!found)
{
// add lib
- to->libraries.insert(sliding_insert_window, lib);
+ libObj.insert("insert", QString("prepend-if-not-exists"));
sliding_insert_window++;
}
+ librariesPlus.prepend(libObj);
+ }
+ obj.insert("+libraries", librariesPlus);
+ obj.insert("mainClass", m_forge_version->mainClass);
+ QString args = m_forge_version->minecraftArguments;
+ QStringList tweakers;
+ {
+ QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
+ QRegularExpressionMatch match = expression.match(args);
+ while (match.hasMatch())
+ {
+ tweakers.append(match.captured(1));
+ args.remove(match.capturedStart(), match.capturedLength());
+ match = expression.match(args);
+ }
+ }
+ if (!args.isEmpty() && args != to->getNonCustomVersion()->minecraftArguments)
+ {
+ obj.insert("minecraftArguments", args);
+ }
+ if (!tweakers.isEmpty())
+ {
+ obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
+ }
+ if (!m_forge_version->processArguments.isEmpty() &&
+ m_forge_version->processArguments != to->getNonCustomVersion()->processArguments)
+ {
+ obj.insert("processArguments", m_forge_version->processArguments);
}
- to->mainClass = m_forge_version->mainClass;
- to->minecraftArguments = m_forge_version->minecraftArguments;
- to->processArguments = m_forge_version->processArguments;
}
- to->externalUpdateFinish();
- return to->toOriginalFile();
+
+ obj.insert("name", QString("Forge"));
+ obj.insert("fileId", id());
+ obj.insert("version", m_forgeVersionString);
+ obj.insert("mcVersion", to->intendedVersionId());
+
+ QFile file(filename(to->instanceRoot()));
+ if (!file.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+ file.write(QJsonDocument(obj).toJson());
+ file.close();
+
+ return true;
}
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
index 0b9f9c77..c5052092 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/ForgeInstaller.h
@@ -14,17 +14,22 @@
*/
#pragma once
+
+#include "BaseInstaller.h"
+
#include <QString>
#include <memory>
class OneSixVersion;
-class ForgeInstaller
+class ForgeInstaller : public BaseInstaller
{
public:
ForgeInstaller(QString filename, QString universal_url);
- bool apply(std::shared_ptr<OneSixVersion> to);
+ bool add(OneSixInstance *to) override;
+
+ QString id() const override { return "net.minecraftforge"; }
private:
// the version, read from the installer
@@ -32,5 +37,6 @@ private:
QString internalPath;
QString finalPath;
QString realVersionId;
+ QString m_forgeVersionString;
QString m_universal_url;
};
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
index 1f1a5879..807bccd0 100644
--- a/logic/InstanceFactory.cpp
+++ b/logic/InstanceFactory.cpp
@@ -24,6 +24,7 @@
#include "OneSixInstance.h"
#include "OneSixFTBInstance.h"
#include "NostalgiaInstance.h"
+#include "OneSixInstance.h"
#include "BaseVersion.h"
#include "MinecraftVersion.h"
@@ -50,13 +51,13 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst
QString inst_type = m_settings->get("InstanceType").toString();
// FIXME: replace with a map lookup, where instance classes register their types
- if (inst_type == "Legacy")
+ if (inst_type == "OneSix")
{
- inst = new LegacyInstance(instDir, m_settings, this);
+ inst = new OneSixInstance(instDir, m_settings, this);
}
- else if (inst_type == "OneSix")
+ else if (inst_type == "Legacy")
{
- inst = new OneSixInstance(instDir, m_settings, this);
+ inst = new LegacyInstance(instDir, m_settings, this);
}
else if (inst_type == "Nostalgia")
{
@@ -101,6 +102,7 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&
switch (mcVer->type)
{
case MinecraftVersion::Legacy:
+ // TODO new instance type
m_settings->set("InstanceType", "Legacy");
inst = new LegacyInstance(instDir, m_settings, this);
inst->setIntendedVersionId(version->descriptor());
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
index 07fffff3..60a43d49 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/LiteLoaderInstaller.cpp
@@ -15,12 +15,19 @@
#include "LiteLoaderInstaller.h"
+#include <QJsonArray>
+#include <QJsonDocument>
+
+#include "logger/QsLog.h"
+
#include "OneSixVersion.h"
#include "OneSixLibrary.h"
+#include "OneSixInstance.h"
QMap<QString, QString> LiteLoaderInstaller::m_launcherWrapperVersionMapping;
-LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion(mcVersion)
+LiteLoaderInstaller::LiteLoaderInstaller()
+ : BaseInstaller()
{
if (m_launcherWrapperVersionMapping.isEmpty())
{
@@ -31,72 +38,59 @@ LiteLoaderInstaller::LiteLoaderInstaller(const QString &mcVersion) : m_mcVersion
}
}
-bool LiteLoaderInstaller::canApply() const
+bool LiteLoaderInstaller::canApply(OneSixInstance *instance) const
{
- return m_launcherWrapperVersionMapping.contains(m_mcVersion);
+ return m_launcherWrapperVersionMapping.contains(instance->intendedVersionId());
}
-bool LiteLoaderInstaller::apply(std::shared_ptr<OneSixVersion> to)
+bool LiteLoaderInstaller::add(OneSixInstance *to)
{
- to->externalUpdateStart();
-
- applyLaunchwrapper(to);
- applyLiteLoader(to);
-
- to->mainClass = "net.minecraft.launchwrapper.Launch";
- if (!to->minecraftArguments.contains(
- " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker"))
+ if (!BaseInstaller::add(to))
{
- to->minecraftArguments.append(
- " --tweakClass com.mumfrey.liteloader.launch.LiteLoaderTweaker");
+ return false;
}
- to->externalUpdateFinish();
- return to->toOriginalFile();
-}
+ QJsonObject obj;
-void LiteLoaderInstaller::applyLaunchwrapper(std::shared_ptr<OneSixVersion> to)
-{
- const QString intendedVersion = m_launcherWrapperVersionMapping[m_mcVersion];
+ obj.insert("mainClass", QString("net.minecraft.launchwrapper.Launch"));
+ obj.insert("+tweakers", QJsonArray::fromStringList(QStringList() << "com.mumfrey.liteloader.launch.LiteLoaderTweaker"));
+ obj.insert("order", 10);
- QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
- while (it.hasNext())
+ QJsonArray libraries;
+
+ // launchwrapper
{
- it.next();
- if (it.value()->rawName().startsWith("net.minecraft:launchwrapper:"))
- {
- if (it.value()->version() >= intendedVersion)
- {
- return;
- }
- else
- {
- it.remove();
- }
- }
+ OneSixLibrary launchwrapperLib("net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[to->intendedVersionId()]);
+ launchwrapperLib.finalize();
+ QJsonObject lwLibObj = launchwrapperLib.toJson();
+ lwLibObj.insert("insert", QString("prepend-if-not-exists"));
+ libraries.append(lwLibObj);
}
- std::shared_ptr<OneSixLibrary> lib(new OneSixLibrary(
- "net.minecraft:launchwrapper:" + m_launcherWrapperVersionMapping[m_mcVersion]));
- lib->finalize();
- to->libraries.prepend(lib);
-}
+ // liteloader
+ {
+ OneSixLibrary liteloaderLib("com.mumfrey:liteloader:" + to->intendedVersionId());
+ liteloaderLib.setBaseUrl("http://dl.liteloader.com/versions/");
+ liteloaderLib.finalize();
+ QJsonObject llLibObj = liteloaderLib.toJson();
+ llLibObj.insert("insert", QString("prepend"));
+ libraries.append(llLibObj);
+ }
-void LiteLoaderInstaller::applyLiteLoader(std::shared_ptr<OneSixVersion> to)
-{
- QMutableListIterator<std::shared_ptr<OneSixLibrary>> it(to->libraries);
- while (it.hasNext())
+ obj.insert("+libraries", libraries);
+ obj.insert("name", QString("LiteLoader"));
+ obj.insert("fileId", id());
+ obj.insert("version", to->intendedVersionId());
+ obj.insert("mcVersion", to->intendedVersionId());
+
+ QFile file(filename(to->instanceRoot()));
+ if (!file.open(QFile::WriteOnly))
{
- it.next();
- if (it.value()->rawName().startsWith("com.mumfrey:liteloader:"))
- {
- it.remove();
- }
+ QLOG_ERROR() << "Error opening" << file.fileName() << "for reading:" << file.errorString();
+ return false;
}
+ file.write(QJsonDocument(obj).toJson());
+ file.close();
- std::shared_ptr<OneSixLibrary> lib(
- new OneSixLibrary("com.mumfrey:liteloader:" + m_mcVersion));
- lib->setBaseUrl("http://dl.liteloader.com/versions/");
- lib->finalize();
- to->libraries.prepend(lib);
+ return true;
}
diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h
index 44b306d6..5e01b16c 100644
--- a/logic/LiteLoaderInstaller.h
+++ b/logic/LiteLoaderInstaller.h
@@ -14,26 +14,22 @@
*/
#pragma once
+
+#include "BaseInstaller.h"
+
#include <QString>
#include <QMap>
-#include <memory>
-
-class OneSixVersion;
-class LiteLoaderInstaller
+class LiteLoaderInstaller : public BaseInstaller
{
public:
- LiteLoaderInstaller(const QString &mcVersion);
+ LiteLoaderInstaller();
- bool canApply() const;
-
- bool apply(std::shared_ptr<OneSixVersion> to);
+ bool canApply(OneSixInstance *instance) const override;
+ bool add(OneSixInstance *to) override;
private:
- QString m_mcVersion;
-
- void applyLaunchwrapper(std::shared_ptr<OneSixVersion> to);
- void applyLiteLoader(std::shared_ptr<OneSixVersion> to);
+ virtual QString id() const override { return "com.mumfrey.liteloader"; }
static QMap<QString, QString> m_launcherWrapperVersionMapping;
};
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index f8e695b9..ca88142a 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -55,15 +55,13 @@ slots:
setStatus(tr("Installing Forge..."));
QString forgePath = entry->getFullPath();
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!instance->reloadFullVersion())
+ if (!instance->reloadVersion())
{
emitFailed(tr("Couldn't load the version config"));
return;
}
- instance->revertCustomVersion();
- instance->customizeVersion();
auto version = instance->getFullVersion();
- if (!forge.apply(version))
+ if (!forge.add(instance))
{
emitFailed(tr("Couldn't install Forge"));
return;
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 67649f77..8ecbe7a0 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -13,32 +13,37 @@
* limitations under the License.
*/
-#include "MultiMC.h"
#include "OneSixInstance.h"
+
+#include <QIcon>
+
#include "OneSixInstance_p.h"
#include "OneSixUpdate.h"
-#include "MinecraftProcess.h"
#include "OneSixVersion.h"
-#include "JavaChecker.h"
-#include "logic/icons/IconList.h"
-
-#include <setting.h>
-#include <pathutils.h>
-#include <cmdutils.h>
-#include <JlCompress.h>
-#include "gui/dialogs/OneSixModEditDialog.h"
+#include "pathutils.h"
#include "logger/QsLog.h"
-#include "logic/assets/AssetsUtils.h"
-#include <QIcon>
+#include "assets/AssetsUtils.h"
+#include "MultiMC.h"
+#include "icons/IconList.h"
+#include "MinecraftProcess.h"
+#include "gui/dialogs/OneSixModEditDialog.h"
-OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj,
- QObject *parent)
- : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent)
+OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
+ : BaseInstance(new OneSixInstancePrivate(), rootDir, settings, parent)
{
I_D(OneSixInstance);
d->m_settings->registerSetting("IntendedVersion", "");
d->m_settings->registerSetting("ShouldUpdate", false);
- reloadFullVersion();
+ d->version.reset(new OneSixVersion(this, this));
+ d->nonCustomVersion.reset(new OneSixVersion(this, this));
+ if (QDir(instanceRoot()).exists("version.json"))
+ {
+ reloadVersion();
+ }
+ else
+ {
+ clearVersion();
+ }
}
std::shared_ptr<Task> OneSixInstance::doUpdate()
@@ -135,6 +140,10 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
I_D(OneSixInstance);
auto version = d->version;
QString args_pattern = version->minecraftArguments;
+ for (auto tweaker : version->tweakers)
+ {
+ args_pattern += " --tweakClass " + tweaker;
+ }
QMap<QString, QString> token_mapping;
// yggdrasil!
@@ -267,11 +276,8 @@ bool OneSixInstance::setIntendedVersionId(QString version)
{
settings().set("IntendedVersion", version);
setShouldUpdate(true);
- auto pathCustom = PathCombine(instanceRoot(), "custom.json");
- auto pathOrig = PathCombine(instanceRoot(), "version.json");
- QFile::remove(pathCustom);
- QFile::remove(pathOrig);
- reloadFullVersion();
+ QFile::remove(PathCombine(instanceRoot(), "version.json"));
+ clearVersion();
return true;
}
@@ -297,9 +303,10 @@ bool OneSixInstance::shouldUpdate() const
bool OneSixInstance::versionIsCustom()
{
- QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
- QFile versionfile(verpath_custom);
- return versionfile.exists();
+ QDir patches(PathCombine(instanceRoot(), "patches/"));
+ return (patches.exists() && patches.count() >= 0)
+ || QFile::exists(PathCombine(instanceRoot(), "custom.json"))
+ || QFile::exists(PathCombine(instanceRoot(), "user.json"));
}
QString OneSixInstance::currentVersionId() const
@@ -307,62 +314,39 @@ QString OneSixInstance::currentVersionId() const
return intendedVersionId();
}
-bool OneSixInstance::customizeVersion()
+bool OneSixInstance::reloadVersion(QWidget *widgetParent)
{
- if (!versionIsCustom())
- {
- auto pathCustom = PathCombine(instanceRoot(), "custom.json");
- auto pathOrig = PathCombine(instanceRoot(), "version.json");
- QFile::copy(pathOrig, pathCustom);
- return reloadFullVersion();
- }
- else
- return true;
-}
+ I_D(OneSixInstance);
-bool OneSixInstance::revertCustomVersion()
-{
- if (versionIsCustom())
+ bool ret = d->version->reload(widgetParent);
+ if (ret)
{
- auto path = PathCombine(instanceRoot(), "custom.json");
- QFile::remove(path);
- return reloadFullVersion();
+ ret = d->nonCustomVersion->reload(widgetParent, true);
}
- else
- return true;
+ emit versionReloaded();
+ return ret;
}
-bool OneSixInstance::reloadFullVersion()
+void OneSixInstance::clearVersion()
{
I_D(OneSixInstance);
-
- QString verpath = PathCombine(instanceRoot(), "version.json");
- {
- QString verpath_custom = PathCombine(instanceRoot(), "custom.json");
- QFile versionfile(verpath_custom);
- if (versionfile.exists())
- verpath = verpath_custom;
- }
-
- auto version = OneSixVersion::fromFile(verpath);
- if (version)
- {
- d->version = version;
- return true;
- }
- else
- {
- d->version.reset();
- return false;
- }
+ d->version->clear();
+ d->nonCustomVersion->clear();
+ emit versionReloaded();
}
-std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion()
+std::shared_ptr<OneSixVersion> OneSixInstance::getFullVersion() const
{
- I_D(OneSixInstance);
+ I_D(const OneSixInstance);
return d->version;
}
+std::shared_ptr<OneSixVersion> OneSixInstance::getNonCustomVersion() const
+{
+ I_D(const OneSixInstance);
+ return d->nonCustomVersion;
+}
+
QString OneSixInstance::defaultBaseJar() const
{
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
@@ -382,7 +366,7 @@ bool OneSixInstance::menuActionEnabled(QString action_name) const
QString OneSixInstance::getStatusbarDescription()
{
- QString descr = "One Six : " + intendedVersionId();
+ QString descr = "OneSix : " + intendedVersionId();
if (versionIsCustom())
{
descr + " (custom)";
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index c159723b..5aa12e44 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -15,21 +15,17 @@
#pragma once
-#include <QStringList>
-#include <QDir>
-
#include "BaseInstance.h"
-class OneSixVersion;
-class Task;
-class ModList;
+#include "OneSixVersion.h"
+#include "ModList.h"
class OneSixInstance : public BaseInstance
{
Q_OBJECT
public:
explicit OneSixInstance(const QString &rootDir, SettingsObject *settings,
- QObject *parent = 0);
+ QObject *parent = 0);
////// Mod Lists //////
std::shared_ptr<ModList> loaderModList();
@@ -55,14 +51,14 @@ public:
virtual QDialog *createModEditDialog(QWidget *parent) override;
- /// reload the full version json file. return true on success!
- bool reloadFullVersion();
+ /// reload the full version json files. return true on success!
+ bool reloadVersion(QWidget *widgetParent = 0);
+ /// clears all version information in preparation for an update
+ void clearVersion();
/// get the current full version info
- std::shared_ptr<OneSixVersion> getFullVersion();
- /// revert the current custom version back to base
- bool revertCustomVersion();
- /// customize the current base version
- bool customizeVersion();
+ std::shared_ptr<OneSixVersion> getFullVersion() const;
+ /// gets the current version info, excluding custom.json
+ std::shared_ptr<OneSixVersion> getNonCustomVersion() const;
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
@@ -72,6 +68,9 @@ public:
virtual bool menuActionEnabled(QString action_name) const override;
virtual QString getStatusbarDescription() override;
+signals:
+ void versionReloaded();
+
private:
QStringList processMinecraftArgs(AuthSessionPtr account);
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index 6b7ea431..6dc74e50 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -15,16 +15,14 @@
#pragma once
-#include <memory>
-
-#include "logic/BaseInstance_p.h"
-#include "logic/OneSixVersion.h"
-#include "logic/OneSixLibrary.h"
-#include "logic/ModList.h"
+#include "BaseInstance_p.h"
+#include "OneSixVersion.h"
+#include "ModList.h"
struct OneSixInstancePrivate : public BaseInstancePrivate
{
std::shared_ptr<OneSixVersion> version;
+ std::shared_ptr<OneSixVersion> nonCustomVersion;
std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> resource_pack_list;
-}; \ No newline at end of file
+};
diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp
index 7b80d5e7..2b1f0600 100644
--- a/logic/OneSixLibrary.cpp
+++ b/logic/OneSixLibrary.cpp
@@ -76,11 +76,11 @@ void OneSixLibrary::finalize()
}
}
-void OneSixLibrary::setName(QString name)
+void OneSixLibrary::setName(const QString &name)
{
m_name = name;
}
-void OneSixLibrary::setBaseUrl(QString base_url)
+void OneSixLibrary::setBaseUrl(const QString &base_url)
{
m_base_url = base_url;
}
@@ -88,50 +88,54 @@ void OneSixLibrary::setIsNative()
{
m_is_native = true;
}
-void OneSixLibrary::addNative(OpSys os, QString suffix)
+void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{
m_is_native = true;
m_native_suffixes[os] = suffix;
}
+void OneSixLibrary::clearSuffixes()
+{
+ m_native_suffixes.clear();
+}
void OneSixLibrary::setRules(QList<std::shared_ptr<Rule>> rules)
{
m_rules = rules;
}
-bool OneSixLibrary::isActive()
+bool OneSixLibrary::isActive() const
{
return m_is_active;
}
-bool OneSixLibrary::isNative()
+bool OneSixLibrary::isNative() const
{
return m_is_native;
}
-QString OneSixLibrary::downloadUrl()
+QString OneSixLibrary::downloadUrl() const
{
if (m_absolute_url.size())
return m_absolute_url;
return m_download_url;
}
-QString OneSixLibrary::storagePath()
+QString OneSixLibrary::storagePath() const
{
return m_storage_path;
}
-void OneSixLibrary::setAbsoluteUrl(QString absolute_url)
+void OneSixLibrary::setAbsoluteUrl(const QString &absolute_url)
{
m_absolute_url = absolute_url;
}
-QString OneSixLibrary::absoluteUrl()
+QString OneSixLibrary::absoluteUrl() const
{
return m_absolute_url;
}
-void OneSixLibrary::setHint(QString hint)
+void OneSixLibrary::setHint(const QString &hint)
{
m_hint = hint;
}
-QString OneSixLibrary::hint()
+QString OneSixLibrary::hint() const
{
return m_hint;
}
@@ -176,7 +180,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "32");
QString origin = PathCombine("libraries", cooked_storage);
QString target_dir_cooked = PathCombine(target_dir, "32");
- if(!ensureFolderPathExists(target_dir_cooked))
+ if (!ensureFolderPathExists(target_dir_cooked))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false;
@@ -191,7 +195,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
cooked_storage.replace("${arch}", "64");
origin = PathCombine("libraries", cooked_storage);
target_dir_cooked = PathCombine(target_dir, "64");
- if(!ensureFolderPathExists(target_dir_cooked))
+ if (!ensureFolderPathExists(target_dir_cooked))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir_cooked;
return false;
@@ -205,7 +209,7 @@ bool OneSixLibrary::extractTo(QString target_dir)
}
else
{
- if(!ensureFolderPathExists(target_dir))
+ if (!ensureFolderPathExists(target_dir))
{
QLOG_ERROR() << "Couldn't create folder " + target_dir;
return false;
@@ -230,8 +234,10 @@ QJsonObject OneSixLibrary::toJson()
libRoot.insert("MMC-hint", m_hint);
if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES &&
- m_base_url != "https://" + URLConstants::LIBRARY_BASE)
+ m_base_url != "https://" + URLConstants::LIBRARY_BASE && !m_base_url.isEmpty())
+ {
libRoot.insert("url", m_base_url);
+ }
if (isNative() && m_native_suffixes.size())
{
QJsonObject nativeList;
diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h
index 227cdbef..5384015a 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/OneSixLibrary.h
@@ -63,7 +63,7 @@ public:
public:
/// Constructor
- OneSixLibrary(QString name)
+ OneSixLibrary(const QString &name)
{
m_name = name;
}
@@ -84,48 +84,50 @@ public:
void finalize();
/// Set the library composite name
- void setName(QString name);
+ void setName(const QString &name);
/// get a decent-looking name
- QString name()
+ QString name() const
{
return m_decentname;
}
/// get a decent-looking version
- QString version()
+ QString version() const
{
return m_decentversion;
}
/// what kind of library is it? (for display)
- QString type()
+ QString type() const
{
return m_decenttype;
}
/// Set the url base for downloads
- void setBaseUrl(QString base_url);
+ void setBaseUrl(const QString &base_url);
/// Call this to mark the library as 'native' (it's a zip archive with DLLs)
void setIsNative();
/// Attach a name suffix to the specified OS native
- void addNative(OpSys os, QString suffix);
+ void addNative(OpSys os, const QString &suffix);
+ /// Clears all suffixes
+ void clearSuffixes();
/// Set the load rules
void setRules(QList<std::shared_ptr<Rule>> rules);
/// Returns true if the library should be loaded (or extracted, in case of natives)
- bool isActive();
+ bool isActive() const;
/// Returns true if the library is native
- bool isNative();
+ bool isNative() const;
/// Get the URL to download the library from
- QString downloadUrl();
+ QString downloadUrl() const;
/// Get the relative path where the library should be saved
- QString storagePath();
+ QString storagePath() const;
/// set an absolute URL for the library. This is an MMC extension.
- void setAbsoluteUrl(QString absolute_url);
- QString absoluteUrl();
+ void setAbsoluteUrl(const QString &absolute_url);
+ QString absoluteUrl() const;
/// set a hint about how to treat the library. This is an MMC extension.
- void setHint(QString hint);
- QString hint();
+ void setHint(const QString &hint);
+ QString hint() const;
bool extractTo(QString target_dir);
bool filesExist();
diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp
index 392b1dd1..d8d13b50 100644
--- a/logic/OneSixRule.cpp
+++ b/logic/OneSixRule.cpp
@@ -18,7 +18,7 @@
#include "OneSixRule.h"
-QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules)
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
{
QList<std::shared_ptr<Rule>> rules;
auto rulesVal = objectWithRules.value("rules");
@@ -86,4 +86,4 @@ RuleAction RuleAction_fromString(QString name)
if (name == "disallow")
return Disallow;
return Defer;
-} \ No newline at end of file
+}
diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h
index 5a13cbd9..426e2886 100644
--- a/logic/OneSixRule.h
+++ b/logic/OneSixRule.h
@@ -27,7 +27,7 @@ enum RuleAction
};
RuleAction RuleAction_fromString(QString);
-QList<std::shared_ptr<Rule>> rulesFromJsonV4(QJsonObject &objectWithRules);
+QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
class Rule
{
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index 7685952c..d3ac80c2 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -131,7 +131,7 @@ void OneSixUpdate::versionFileFinished()
{
finfo.remove();
}
- inst->reloadFullVersion();
+ inst->reloadVersion();
jarlibStart();
}
@@ -229,7 +229,7 @@ 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->reloadFullVersion();
+ bool successful = inst->reloadVersion();
if (!successful)
{
emitFailed("Failed to load the version description file. It might be "
diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp
index 8ae685f0..0b6d1986 100644
--- a/logic/OneSixVersion.cpp
+++ b/logic/OneSixVersion.cpp
@@ -13,228 +13,86 @@
* limitations under the License.
*/
-#include "logic/OneSixVersion.h"
-#include "logic/OneSixLibrary.h"
-#include "logic/OneSixRule.h"
+#include "OneSixVersion.h"
-#include "logger/QsLog.h"
+#include <QDebug>
+#include <QFile>
-std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root,
- std::shared_ptr<OneSixVersion> fullVersion)
-{
- fullVersion->id = root.value("id").toString();
-
- fullVersion->mainClass = root.value("mainClass").toString();
- auto procArgsValue = root.value("processArguments");
- if (procArgsValue.isString())
- {
- fullVersion->processArguments = procArgsValue.toString();
- QString toCompare = fullVersion->processArguments.toLower();
- if (toCompare == "legacy")
- {
- fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- fullVersion->minecraftArguments =
- "--username ${auth_player_name} --session ${auth_session}";
- }
- else if (toCompare == "username_session_version")
- {
- fullVersion->minecraftArguments = "--username ${auth_player_name} "
- "--session ${auth_session} "
- "--version ${profile_name}";
- }
- }
-
- auto minecraftArgsValue = root.value("minecraftArguments");
- if (minecraftArgsValue.isString())
- {
- fullVersion->minecraftArguments = minecraftArgsValue.toString();
- }
+#include "OneSixVersionBuilder.h"
- auto minecraftTypeValue = root.value("type");
- if (minecraftTypeValue.isString())
- {
- fullVersion->type = minecraftTypeValue.toString();
- }
-
- fullVersion->releaseTime = root.value("releaseTime").toString();
- fullVersion->time = root.value("time").toString();
-
- auto assetsID = root.value("assets");
- if (assetsID.isString())
- {
- fullVersion->assets = assetsID.toString();
- }
- else
- {
- fullVersion->assets = "legacy";
- }
-
- QLOG_DEBUG() << "Assets version:" << fullVersion->assets;
-
- // Iterate through the list, if it's a list.
- auto librariesValue = root.value("libraries");
- if (!librariesValue.isArray())
- return fullVersion;
-
- QJsonArray libList = root.value("libraries").toArray();
- for (auto libVal : libList)
- {
- if (!libVal.isObject())
- {
- continue;
- }
-
- QJsonObject libObj = libVal.toObject();
-
- // Library name
- auto nameVal = libObj.value("name");
- if (!nameVal.isString())
- continue;
- std::shared_ptr<OneSixLibrary> library(new OneSixLibrary(nameVal.toString()));
-
- auto urlVal = libObj.value("url");
- if (urlVal.isString())
- {
- library->setBaseUrl(urlVal.toString());
- }
- auto hintVal = libObj.value("MMC-hint");
- if (hintVal.isString())
- {
- library->setHint(hintVal.toString());
- }
- auto urlAbsVal = libObj.value("MMC-absoluteUrl");
- auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility
- if (urlAbsVal.isString())
- {
- library->setAbsoluteUrl(urlAbsVal.toString());
- }
- else if (urlAbsuVal.isString())
- {
- library->setAbsoluteUrl(urlAbsuVal.toString());
- }
- // Extract excludes (if any)
- auto extractVal = libObj.value("extract");
- if (extractVal.isObject())
- {
- QStringList excludes;
- auto extractObj = extractVal.toObject();
- auto excludesVal = extractObj.value("exclude");
- if (excludesVal.isArray())
- {
- auto excludesList = excludesVal.toArray();
- for (auto excludeVal : excludesList)
- {
- if (excludeVal.isString())
- excludes.append(excludeVal.toString());
- }
- library->extract_excludes = excludes;
- }
- }
-
- auto nativesVal = libObj.value("natives");
- if (nativesVal.isObject())
- {
- library->setIsNative();
- auto nativesObj = nativesVal.toObject();
- auto iter = nativesObj.begin();
- while (iter != nativesObj.end())
- {
- auto osType = OpSys_fromString(iter.key());
- if (osType == Os_Other)
- continue;
- if (!iter.value().isString())
- continue;
- library->addNative(osType, iter.value().toString());
- iter++;
- }
- }
- library->setRules(rulesFromJsonV4(libObj));
- library->finalize();
- fullVersion->libraries.append(library);
- }
- return fullVersion;
+OneSixVersion::OneSixVersion(OneSixInstance *instance, QObject *parent)
+ : QAbstractListModel(parent), m_instance(instance)
+{
+ clear();
}
-std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root)
+bool OneSixVersion::reload(QWidget *widgetParent, const bool excludeCustom)
{
- std::shared_ptr<OneSixVersion> readVersion(new OneSixVersion());
- int launcher_ver = readVersion->minimumLauncherVersion =
- root.value("minimumLauncherVersion").toDouble();
-
- // ADD MORE HERE :D
- if (launcher_ver > 0 && launcher_ver <= 13)
- return fromJsonV4(root, readVersion);
- else
- {
- return std::shared_ptr<OneSixVersion>();
- }
+ beginResetModel();
+ bool ret = OneSixVersionBuilder::build(this, m_instance, widgetParent, excludeCustom);
+ endResetModel();
+ return ret;
}
-std::shared_ptr<OneSixVersion> OneSixVersion::fromFile(QString filepath)
+void OneSixVersion::clear()
{
- QFile file(filepath);
- if (!file.open(QIODevice::ReadOnly))
- {
- return std::shared_ptr<OneSixVersion>();
- }
-
- auto data = file.readAll();
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ 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();
+}
- if (jsonError.error != QJsonParseError::NoError)
+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)
{
- return std::shared_ptr<OneSixVersion>();
+ qDebug().nospace() << "\n\t\t" << lib.get();
}
+ qDebug().nospace() << "\n)";
+}
- if (!jsonDoc.isObject())
+bool OneSixVersion::canRemove(const int index) const
+{
+ if (index < versionFiles.size())
{
- return std::shared_ptr<OneSixVersion>();
+ return versionFiles.at(index).id != "org.multimc.version.json";
}
- QJsonObject root = jsonDoc.object();
- auto version = fromJson(root);
- if (version)
- version->original_file = filepath;
- return version;
+ return false;
}
-bool OneSixVersion::toOriginalFile()
+bool OneSixVersion::remove(const int index)
{
- if (original_file.isEmpty())
- return false;
- QSaveFile file(original_file);
- if (!file.open(QIODevice::WriteOnly))
+ if (canRemove(index))
{
- return false;
+ return QFile::remove(versionFiles.at(index).filename);
}
- // serialize base attributes (those we care about anyway)
- QJsonObject root;
- root.insert("minecraftArguments", minecraftArguments);
- root.insert("mainClass", mainClass);
- root.insert("minimumLauncherVersion", minimumLauncherVersion);
- root.insert("time", time);
- root.insert("id", id);
- root.insert("type", type);
- // screw processArguments
- root.insert("releaseTime", releaseTime);
- QJsonArray libarray;
- for (const auto &lib : libraries)
- {
- libarray.append(lib->toJson());
- }
- if (libarray.count())
- root.insert("libraries", libarray);
- QJsonDocument doc(root);
- file.write(doc.toJson());
- return file.commit();
+ return false;
}
-QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs()
+QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNormalLibs()
{
- QList<std::shared_ptr<OneSixLibrary>> output;
+ QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries)
{
if (lib->isActive() && !lib->isNative())
@@ -245,9 +103,9 @@ QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNormalLibs()
return output;
}
-QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
+QList<std::shared_ptr<OneSixLibrary> > OneSixVersion::getActiveNativeLibs()
{
- QList<std::shared_ptr<OneSixLibrary>> output;
+ QList<std::shared_ptr<OneSixLibrary> > output;
for (auto lib : libraries)
{
if (lib->isActive() && lib->isNative())
@@ -258,14 +116,14 @@ QList<std::shared_ptr<OneSixLibrary>> OneSixVersion::getActiveNativeLibs()
return output;
}
-void OneSixVersion::externalUpdateStart()
-{
- beginResetModel();
-}
-
-void OneSixVersion::externalUpdateFinish()
+std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(const QJsonObject &obj)
{
- endResetModel();
+ 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
@@ -276,7 +134,7 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
int row = index.row();
int column = index.column();
- if (row < 0 || row >= libraries.size())
+ if (row < 0 || row >= versionFiles.size())
return QVariant();
if (role == Qt::DisplayRole)
@@ -284,11 +142,9 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
switch (column)
{
case 0:
- return libraries[row]->name();
+ return versionFiles.at(row).name;
case 1:
- return libraries[row]->type();
- case 2:
- return libraries[row]->version();
+ return versionFiles.at(row).version;
default:
return QVariant();
}
@@ -296,45 +152,61 @@ QVariant OneSixVersion::data(const QModelIndex &index, int role) const
return QVariant();
}
-Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
+QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
{
- if (!index.isValid())
- return Qt::NoItemFlags;
- int row = index.row();
- if (libraries[row]->isActive())
- {
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
- }
- else
+ if (orientation == Qt::Horizontal)
{
- return Qt::ItemNeverHasChildren;
+ if (role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case 0:
+ return tr("Name");
+ case 1:
+ return tr("Version");
+ default:
+ return QVariant();
+ }
+ }
}
- // return QAbstractListModel::flags(index);
+ return QVariant();
}
-QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const
+Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const
{
- if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
- return QVariant();
- switch (section)
- {
- case 0:
- return QString("Name");
- case 1:
- return QString("Type");
- case 2:
- return QString("Version");
- default:
- return QString();
- }
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
int OneSixVersion::rowCount(const QModelIndex &parent) const
{
- return libraries.size();
+ return versionFiles.size();
}
int OneSixVersion::columnCount(const QModelIndex &parent) const
{
- return 3;
+ 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/OneSixVersion.h b/logic/OneSixVersion.h
index 036f3d53..d7a6684d 100644
--- a/logic/OneSixVersion.h
+++ b/logic/OneSixVersion.h
@@ -14,40 +14,48 @@
*/
#pragma once
-#include <QtCore>
+
+#include <QAbstractListModel>
+
+#include <QString>
+#include <QList>
#include <memory>
-class OneSixLibrary;
+#include "OneSixLibrary.h"
+
+class OneSixInstance;
class OneSixVersion : public QAbstractListModel
{
- // Things required to implement the Qt list model
+ Q_OBJECT
public:
+ explicit OneSixVersion(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;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
- virtual QVariant headerData(int section, Qt::Orientation orientation,
- int role = Qt::DisplayRole) const;
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- // serialization/deserialization
-public:
- bool toOriginalFile();
- static std::shared_ptr<OneSixVersion> fromJson(QJsonObject root);
- static std::shared_ptr<OneSixVersion> fromFile(QString filepath);
+ bool reload(QWidget *widgetParent, const bool excludeCustom = false);
+ void clear();
+
+ void dump() const;
+
+ bool canRemove(const int index) const;
+
+public
+slots:
+ bool remove(const int index);
public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
- // called when something starts/stops messing with the object
- // FIXME: these are ugly in every possible way.
- void externalUpdateStart();
- void externalUpdateFinish();
+
+ static std::shared_ptr<OneSixVersion> fromJson(const QJsonObject &obj);
// data members
public:
- /// file this was read from. blank, if none
- QString original_file;
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id;
/// Last updated time - as a string
@@ -76,6 +84,10 @@ public:
*/
int minimumLauncherVersion = 0xDEADBEEF;
/**
+ * A list of all tweaker classes
+ */
+ QStringList tweakers;
+ /**
* The main class to load first
*/
QString mainClass;
@@ -103,4 +115,20 @@ public:
}
*/
// QList<Rule> rules;
+
+ struct VersionFile
+ {
+ QString name;
+ QString id;
+ QString version;
+ QString mcVersion;
+ QString filename;
+ };
+ QList<VersionFile> versionFiles;
+
+private:
+ OneSixInstance *m_instance;
};
+
+QDebug operator<<(QDebug &dbg, const OneSixVersion *version);
+QDebug operator<<(QDebug &dbg, const OneSixLibrary *library);
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
new file mode 100644
index 00000000..a541766b
--- /dev/null
+++ b/logic/OneSixVersionBuilder.cpp
@@ -0,0 +1,919 @@
+/* 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 "OneSixVersionBuilder.h"
+
+#include <QList>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QFile>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QObject>
+#include <QDir>
+#include <QDebug>
+
+#include "OneSixVersion.h"
+#include "OneSixInstance.h"
+#include "OneSixRule.h"
+#include "logger/QsLog.h"
+
+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,
+ AppendIfNotExists,
+ PrependIfNotExists,
+ Replace
+ };
+ InsertType insertType;
+ QString insertData;
+ };
+ bool shouldOverwriteLibs = false;
+ QList<Library> overwriteLibs;
+ QList<Library> addLibs;
+ QList<QString> removeLibs;
+
+ 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)
+ {
+ 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();
+ }
+ }
+ };
+
+ 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 = true;
+ 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;
+ }
+ 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 == "append")
+ {
+ lib.insertType = Library::Append;
+ }
+ else if (insertString == "prepend")
+ {
+ lib.insertType = Library::Prepend;
+ }
+ else if (insertString == "prepend-if-not-exists")
+ {
+ lib.insertType = Library::PrependIfNotExists;
+ }
+ else if (insertString == "append-if-not-exists")
+ {
+ lib.insertType = Library::PrependIfNotExists;
+ }
+ else if (insertString == "replace")
+ {
+ lib.insertType = Library::Replace;
+ }
+ else
+ {
+ QLOG_ERROR() << "A '+' library in" << filename
+ << "contains an invalid insert 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));
+ out->setBaseUrl(lib.url);
+ out->setHint(lib.hint);
+ 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;
+ }
+ void applyTo(OneSixVersion *version, bool &isError)
+ {
+ isError = true;
+ 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:
+ version->libraries.append(createLibrary(lib));
+ break;
+ case Library::Prepend:
+ version->libraries.prepend(createLibrary(lib));
+ break;
+ case Library::AppendIfNotExists:
+ {
+
+ int index = findLibrary(version->libraries, lib.name);
+ if (index < 0)
+ {
+ version->libraries.append(createLibrary(lib));
+ }
+ break;
+ }
+ case Library::PrependIfNotExists:
+ {
+
+ int index = findLibrary(version->libraries, lib.name);
+ if (index < 0)
+ {
+ version->libraries.prepend(createLibrary(lib));
+ }
+ 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;
+ version->versionFiles.append(versionFile);
+
+ isError = false;
+ }
+};
+
+OneSixVersionBuilder::OneSixVersionBuilder()
+{
+}
+
+bool OneSixVersionBuilder::build(OneSixVersion *version, OneSixInstance *instance,
+ QWidget *widgetParent, const bool excludeCustom)
+{
+ OneSixVersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = instance;
+ builder.m_widgetParent = widgetParent;
+ return builder.build(excludeCustom);
+}
+
+bool OneSixVersionBuilder::read(OneSixVersion *version, const QJsonObject &obj)
+{
+ OneSixVersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = 0;
+ builder.m_widgetParent = 0;
+ return builder.read(obj);
+}
+
+bool OneSixVersionBuilder::build(const bool excludeCustom)
+{
+ m_version->clear();
+
+ QDir root(m_instance->instanceRoot());
+ QDir patches(root.absoluteFilePath("patches/"));
+
+ 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;
+ }
+ bool isError = false;
+ file.applyTo(m_version, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr(
+ "Error while applying %1. Please check MultiMC-0.log for more info.")
+ .arg(root.absoluteFilePath("custom.json")));
+ return false;
+ }
+ }
+ else
+ {
+ // version.json -> patches/*.json -> instance.json
+
+ // 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();
+ bool isError = false;
+ file.applyTo(m_version, isError);
+ if (isError)
+ {
+ 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;
+ }
+ }
+
+ // 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;
+ }
+ files.insert(file.order, qMakePair(info.fileName(), file));
+ }
+ for (auto order : files.keys())
+ {
+ QLOG_DEBUG() << "Applying file with order" << order;
+ auto filePair = files[order];
+ bool isError = false;
+ filePair.second.applyTo(m_version, isError);
+ if (isError)
+ {
+ 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;
+ }
+ }
+ }
+
+ // user.json
+ if (!excludeCustom)
+ {
+ if (QFile::exists(root.absoluteFilePath("user.json")))
+ {
+ QLOG_INFO() << "Reading user.json";
+ VersionFile file;
+ if (!read(QFileInfo(root.absoluteFilePath("user.json")), false, &file))
+ {
+ return false;
+ }
+ file.name = "user.json";
+ file.fileId = "org.multimc.user.json";
+ file.version = QString();
+ file.mcVersion = QString();
+ bool isError = false;
+ file.applyTo(m_version, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr(
+ "Error while applying %1. Please check MultiMC-0.log for more info.")
+ .arg(root.absoluteFilePath("user.json")));
+ return false;
+ }
+ }
+ }
+ }
+
+ // some final touches
+ {
+ 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->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}";
+ }
+ }
+ }
+
+ return true;
+}
+
+bool OneSixVersionBuilder::read(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;
+ }
+ file.applyTo(m_version, isError);
+ if (isError)
+ {
+ QMessageBox::critical(
+ m_widgetParent, QObject::tr("Error"),
+ QObject::tr("Error while applying. Please check MultiMC-0.log for more info."));
+ return false;
+ }
+
+ return true;
+}
+
+bool OneSixVersionBuilder::read(const QFileInfo &fileInfo, const bool requireOrder,
+ VersionFile *out)
+{
+ 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;
+ }
+ 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;
+ }
+ bool isError = false;
+ *out = VersionFile::fromJson(doc, file.fileName(), requireOrder, isError);
+ 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;
+}
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
new file mode 100644
index 00000000..ac8c13bf
--- /dev/null
+++ b/logic/OneSixVersionBuilder.h
@@ -0,0 +1,43 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <QString>
+
+class OneSixVersion;
+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 excludeCustom);
+ static bool read(OneSixVersion *version, const QJsonObject &obj);
+
+private:
+ OneSixVersion *m_version;
+ OneSixInstance *m_instance;
+ QWidget *m_widgetParent;
+
+ bool build(const bool excludeCustom);
+ bool read(const QJsonObject &obj);
+
+ bool read(const QFileInfo &fileInfo, const bool requireOrder, VersionFile *out);
+};