summaryrefslogtreecommitdiffstats
path: root/logic
diff options
context:
space:
mode:
Diffstat (limited to 'logic')
-rw-r--r--logic/BaseInstaller.cpp9
-rw-r--r--logic/BaseInstaller.h2
-rw-r--r--logic/BaseInstance.cpp16
-rw-r--r--logic/BaseInstance.h27
-rw-r--r--logic/BaseInstance_p.h1
-rw-r--r--logic/BaseVersion.h4
-rw-r--r--logic/BaseVersionList.cpp (renamed from logic/lists/BaseVersionList.cpp)2
-rw-r--r--logic/BaseVersionList.h (renamed from logic/lists/BaseVersionList.h)0
-rw-r--r--logic/InstanceFactory.cpp79
-rw-r--r--logic/InstanceLauncher.cpp2
-rw-r--r--logic/InstanceList.cpp (renamed from logic/lists/InstanceList.cpp)28
-rw-r--r--logic/InstanceList.h (renamed from logic/lists/InstanceList.h)0
-rw-r--r--logic/LegacyFTBInstance.cpp5
-rw-r--r--logic/LegacyFTBInstance.h1
-rw-r--r--logic/LegacyInstance.cpp57
-rw-r--r--logic/LegacyInstance.h14
-rw-r--r--logic/LegacyUpdate.cpp85
-rw-r--r--logic/LegacyUpdate.h9
-rw-r--r--logic/LwjglVersionList.cpp (renamed from logic/lists/LwjglVersionList.cpp)0
-rw-r--r--logic/LwjglVersionList.h (renamed from logic/lists/LwjglVersionList.h)0
-rw-r--r--logic/MMCJson.cpp24
-rw-r--r--logic/MMCJson.h19
-rw-r--r--logic/MinecraftProcess.cpp11
-rw-r--r--logic/MinecraftVersion.h89
-rw-r--r--logic/ModList.cpp1
-rw-r--r--logic/NostalgiaInstance.cpp36
-rw-r--r--logic/NostalgiaInstance.h29
-rw-r--r--logic/OneSixFTBInstance.cpp16
-rw-r--r--logic/OneSixFTBInstance.h1
-rw-r--r--logic/OneSixInstance.cpp301
-rw-r--r--logic/OneSixInstance.h44
-rw-r--r--logic/OneSixInstance_p.h13
-rw-r--r--logic/OneSixUpdate.cpp357
-rw-r--r--logic/OneSixUpdate.h21
-rw-r--r--logic/OneSixVersionBuilder.cpp252
-rw-r--r--logic/OneSixVersionBuilder.h47
-rw-r--r--logic/URNResolver.cpp98
-rw-r--r--logic/URNResolver.h18
-rw-r--r--logic/VersionFile.h127
-rw-r--r--logic/VersionFilterData.cpp68
-rw-r--r--logic/VersionFilterData.h26
-rw-r--r--logic/VersionFinal.cpp377
-rw-r--r--logic/forge/ForgeInstaller.cpp (renamed from logic/ForgeInstaller.cpp)194
-rw-r--r--logic/forge/ForgeInstaller.h (renamed from logic/ForgeInstaller.h)22
-rw-r--r--logic/forge/ForgeMirror.h (renamed from logic/net/ForgeMirror.h)0
-rw-r--r--logic/forge/ForgeMirrors.cpp (renamed from logic/net/ForgeMirrors.cpp)0
-rw-r--r--logic/forge/ForgeMirrors.h (renamed from logic/net/ForgeMirrors.h)8
-rw-r--r--logic/forge/ForgeVersion.cpp55
-rw-r--r--logic/forge/ForgeVersion.h33
-rw-r--r--logic/forge/ForgeVersionList.cpp (renamed from logic/lists/ForgeVersionList.cpp)84
-rw-r--r--logic/forge/ForgeVersionList.h (renamed from logic/lists/ForgeVersionList.h)53
-rw-r--r--logic/forge/ForgeXzDownload.cpp (renamed from logic/net/ForgeXzDownload.cpp)0
-rw-r--r--logic/forge/ForgeXzDownload.h (renamed from logic/net/ForgeXzDownload.h)4
-rw-r--r--logic/forge/LegacyForge.cpp (renamed from logic/LegacyForge.cpp)0
-rw-r--r--logic/forge/LegacyForge.h (renamed from logic/LegacyForge.h)2
-rw-r--r--logic/java/JavaChecker.cpp (renamed from logic/JavaChecker.cpp)0
-rw-r--r--logic/java/JavaChecker.h (renamed from logic/JavaChecker.h)0
-rw-r--r--logic/java/JavaCheckerJob.cpp (renamed from logic/JavaCheckerJob.cpp)0
-rw-r--r--logic/java/JavaCheckerJob.h (renamed from logic/JavaCheckerJob.h)0
-rw-r--r--logic/java/JavaUtils.cpp (renamed from logic/JavaUtils.cpp)18
-rw-r--r--logic/java/JavaUtils.h (renamed from logic/JavaUtils.h)2
-rw-r--r--logic/java/JavaVersionList.cpp (renamed from logic/lists/JavaVersionList.cpp)10
-rw-r--r--logic/java/JavaVersionList.h (renamed from logic/lists/JavaVersionList.h)4
-rw-r--r--logic/lists/MinecraftVersionList.cpp290
-rw-r--r--logic/liteloader/LiteLoaderInstaller.cpp (renamed from logic/LiteLoaderInstaller.cpp)8
-rw-r--r--logic/liteloader/LiteLoaderInstaller.h (renamed from logic/LiteLoaderInstaller.h)5
-rw-r--r--logic/liteloader/LiteLoaderVersionList.cpp (renamed from logic/lists/LiteLoaderVersionList.cpp)0
-rw-r--r--logic/liteloader/LiteLoaderVersionList.h (renamed from logic/lists/LiteLoaderVersionList.h)6
-rw-r--r--logic/minecraft/InstanceVersion.cpp537
-rw-r--r--logic/minecraft/InstanceVersion.h (renamed from logic/VersionFinal.h)73
-rw-r--r--logic/minecraft/JarMod.cpp56
-rw-r--r--logic/minecraft/JarMod.h18
-rw-r--r--logic/minecraft/MinecraftVersion.cpp143
-rw-r--r--logic/minecraft/MinecraftVersion.h103
-rw-r--r--logic/minecraft/MinecraftVersionList.cpp602
-rw-r--r--logic/minecraft/MinecraftVersionList.h (renamed from logic/lists/MinecraftVersionList.h)42
-rw-r--r--logic/minecraft/OneSixLibrary.cpp (renamed from logic/OneSixLibrary.cpp)79
-rw-r--r--logic/minecraft/OneSixLibrary.h (renamed from logic/OneSixLibrary.h)34
-rw-r--r--logic/minecraft/OneSixRule.cpp (renamed from logic/OneSixRule.cpp)17
-rw-r--r--logic/minecraft/OneSixRule.h (renamed from logic/OneSixRule.h)8
-rw-r--r--logic/minecraft/OpSys.cpp (renamed from logic/OpSys.cpp)0
-rw-r--r--logic/minecraft/OpSys.h (renamed from logic/OpSys.h)0
-rw-r--r--logic/minecraft/ParseUtils.cpp24
-rw-r--r--logic/minecraft/ParseUtils.h14
-rw-r--r--logic/minecraft/RawLibrary.cpp205
-rw-r--r--logic/minecraft/RawLibrary.h64
-rw-r--r--logic/minecraft/VersionBuildError.h58
-rw-r--r--logic/minecraft/VersionBuilder.cpp349
-rw-r--r--logic/minecraft/VersionBuilder.h56
-rw-r--r--logic/minecraft/VersionFile.cpp (renamed from logic/VersionFile.cpp)378
-rw-r--r--logic/minecraft/VersionFile.h103
-rw-r--r--logic/minecraft/VersionPatch.h31
-rw-r--r--logic/minecraft/VersionSource.h9
-rw-r--r--logic/net/NetJob.h1
-rw-r--r--logic/screenshots/ImgurAlbumCreation.cpp4
-rw-r--r--logic/screenshots/ImgurUpload.cpp9
-rw-r--r--logic/screenshots/Screenshot.cpp14
-rw-r--r--logic/screenshots/Screenshot.h16
-rw-r--r--logic/screenshots/ScreenshotList.cpp113
-rw-r--r--logic/screenshots/ScreenshotList.h70
-rw-r--r--logic/tasks/ProgressProvider.h1
101 files changed, 3940 insertions, 2405 deletions
diff --git a/logic/BaseInstaller.cpp b/logic/BaseInstaller.cpp
index 669fd0ac..5660eb07 100644
--- a/logic/BaseInstaller.cpp
+++ b/logic/BaseInstaller.cpp
@@ -13,15 +13,10 @@
* limitations under the License.
*/
-#include "BaseInstaller.h"
-
#include <QFile>
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
-
-#include "cmdutils.h"
+#include "logic/BaseInstaller.h"
+#include "logic/OneSixInstance.h"
BaseInstaller::BaseInstaller()
{
diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h
index d59833cc..9531fbff 100644
--- a/logic/BaseInstaller.h
+++ b/logic/BaseInstaller.h
@@ -29,7 +29,7 @@ class BaseInstaller
{
public:
BaseInstaller();
-
+ virtual ~BaseInstaller(){};
bool isApplied(OneSixInstance *on);
virtual bool add(OneSixInstance *to);
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index 5fa62593..15bf5ab6 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -27,7 +27,7 @@
#include "pathutils.h"
#include <cmdutils.h>
-#include "lists/MinecraftVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
#include "logic/icons/IconList.h"
BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
@@ -58,6 +58,8 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
// Java Settings
settings().registerSetting("OverrideJava", false);
+ settings().registerSetting("OverrideJavaLocation", false);
+ settings().registerSetting("OverrideJavaArgs", false);
settings().registerOverride(globalSettings->getSetting("JavaPath"));
settings().registerOverride(globalSettings->getSetting("JvmArgs"));
@@ -104,6 +106,18 @@ QString BaseInstance::id() const
return QFileInfo(instanceRoot()).fileName();
}
+bool BaseInstance::isRunning() const
+{
+ I_D(BaseInstance);
+ return d->m_isRunning;
+}
+
+void BaseInstance::setRunning(bool running) const
+{
+ I_D(BaseInstance);
+ d->m_isRunning = running;
+}
+
QString BaseInstance::instanceType() const
{
I_D(BaseInstance);
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 16791592..5f5378e7 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -22,9 +22,10 @@
#include <settingsobject.h>
#include "inifile.h"
-#include "lists/BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/auth/MojangAccount.h"
+class ModList;
class QDialog;
class QDir;
class Task;
@@ -64,6 +65,9 @@ public:
/// be unique.
virtual QString id() const;
+ virtual void setRunning(bool running) const;
+ virtual bool isRunning() const;
+
/// get the type of this instance
QString instanceType() const;
@@ -89,7 +93,7 @@ public:
void setGroupInitial(QString val);
void setGroupPost(QString val);
- QStringList extraArguments() const;
+ virtual QStringList extraArguments() const;
virtual QString intendedVersionId() const = 0;
virtual bool setIntendedVersionId(QString version) = 0;
@@ -110,6 +114,19 @@ public:
virtual bool shouldUpdate() const = 0;
virtual void setShouldUpdate(bool val) = 0;
+ ////// Mod Lists //////
+ virtual std::shared_ptr<ModList> resourcePackList()
+ {
+ return nullptr;
+ }
+ virtual std::shared_ptr<ModList> texturePackList()
+ {
+ return nullptr;
+ }
+
+ /// Traits. Normally inside the version, depends on instance implementation.
+ virtual QSet <QString> traits() = 0;
+
/// Get the curent base jar of this instance. By default, it's the
/// versions/$version/$version.jar
QString baseJar() const;
@@ -169,12 +186,6 @@ public:
/// 'prepareForLaunch'
virtual void cleanupAfterRun() = 0;
- /// create a mod edit dialog for the instance
- virtual QDialog *createModEditDialog(QWidget *parent) = 0;
-
- /// is a particular action enabled with this instance selected?
- virtual bool menuActionEnabled(QString action_name) const = 0;
-
virtual QString getStatusbarDescription() = 0;
/// FIXME: this really should be elsewhere...
diff --git a/logic/BaseInstance_p.h b/logic/BaseInstance_p.h
index 999ff545..6498454f 100644
--- a/logic/BaseInstance_p.h
+++ b/logic/BaseInstance_p.h
@@ -32,4 +32,5 @@ public:
QString m_group;
std::shared_ptr<SettingsObject> m_settings;
QSet<BaseInstance::InstanceFlag> m_flags;
+ bool m_isRunning = false;
};
diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h
index 43f5942a..ed63f551 100644
--- a/logic/BaseVersion.h
+++ b/logic/BaseVersion.h
@@ -16,6 +16,8 @@
#pragma once
#include <memory>
+#include <QString>
+#include <QMetaType>
/*!
* An abstract base class for versions.
@@ -52,4 +54,4 @@ struct BaseVersion
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
-Q_DECLARE_METATYPE(BaseVersionPtr) \ No newline at end of file
+Q_DECLARE_METATYPE(BaseVersionPtr)
diff --git a/logic/lists/BaseVersionList.cpp b/logic/BaseVersionList.cpp
index 6e2c5282..b34750b5 100644
--- a/logic/lists/BaseVersionList.cpp
+++ b/logic/BaseVersionList.cpp
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-#include "logic/lists/BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/BaseVersion.h"
BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
diff --git a/logic/lists/BaseVersionList.h b/logic/BaseVersionList.h
index 21b44e8d..21b44e8d 100644
--- a/logic/lists/BaseVersionList.h
+++ b/logic/BaseVersionList.h
diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp
index 95fd855b..c0a392e0 100644
--- a/logic/InstanceFactory.cpp
+++ b/logic/InstanceFactory.cpp
@@ -13,28 +13,26 @@
* limitations under the License.
*/
-#include "InstanceFactory.h"
-
#include <QDir>
#include <QFileInfo>
-
-#include "BaseInstance.h"
-#include "LegacyInstance.h"
-#include "LegacyFTBInstance.h"
-#include "OneSixInstance.h"
-#include "OneSixFTBInstance.h"
-#include "NostalgiaInstance.h"
-#include "OneSixInstance.h"
-#include "BaseVersion.h"
-#include "MinecraftVersion.h"
-
-#include "inifile.h"
+#include <inifile.h>
#include <inisettingsobject.h>
#include <setting.h>
-#include "pathutils.h"
+#include <pathutils.h>
#include "logger/QsLog.h"
+#include "logic/InstanceFactory.h"
+
+#include "logic/BaseInstance.h"
+#include "logic/LegacyInstance.h"
+#include "logic/LegacyFTBInstance.h"
+#include "logic/OneSixInstance.h"
+#include "logic/OneSixFTBInstance.h"
+#include "logic/OneSixInstance.h"
+#include "logic/BaseVersion.h"
+#include "logic/minecraft/MinecraftVersion.h"
+
InstanceFactory InstanceFactory::loader;
InstanceFactory::InstanceFactory() : QObject(NULL)
@@ -51,7 +49,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(InstancePtr &inst,
QString inst_type = m_settings->get("InstanceType").toString();
// FIXME: replace with a map lookup, where instance classes register their types
- if (inst_type == "OneSix")
+ if (inst_type == "OneSix" || inst_type == "Nostalgia")
{
inst.reset(new OneSixInstance(instDir, m_settings, this));
}
@@ -59,10 +57,6 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(InstancePtr &inst,
{
inst.reset(new LegacyInstance(instDir, m_settings, this));
}
- else if (inst_type == "Nostalgia")
- {
- inst.reset(new NostalgiaInstance(instDir, m_settings, this));
- }
else if (inst_type == "LegacyFTB")
{
inst.reset(new LegacyFTBInstance(instDir, m_settings, this));
@@ -98,55 +92,26 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(InstancePtr &in
if (type == NormalInst)
{
- switch (mcVer->type)
- {
- case MinecraftVersion::Legacy:
- // TODO new instance type
- m_settings->set("InstanceType", "Legacy");
- inst.reset(new LegacyInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::OneSix:
- m_settings->set("InstanceType", "OneSix");
- inst.reset(new OneSixInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::Nostalgia:
- m_settings->set("InstanceType", "Nostalgia");
- inst.reset(new NostalgiaInstance(instDir, m_settings, this));
- inst->setIntendedVersionId(version->descriptor());
- inst->setShouldUseCustomBaseJar(false);
- break;
- default:
- {
- delete m_settings;
- return InstanceFactory::NoSuchVersion;
- }
- }
+ m_settings->set("InstanceType", "OneSix");
+ inst.reset(new OneSixInstance(instDir, m_settings, this));
+ inst->setIntendedVersionId(version->descriptor());
+ inst->setShouldUseCustomBaseJar(false);
}
else if (type == FTBInstance)
{
- switch (mcVer->type)
+ if(mcVer->usesLegacyLauncher())
{
- case MinecraftVersion::Legacy:
m_settings->set("InstanceType", "LegacyFTB");
inst.reset(new LegacyFTBInstance(instDir, m_settings, this));
inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
- break;
- case MinecraftVersion::OneSix:
+ }
+ else
+ {
m_settings->set("InstanceType", "OneSixFTB");
inst.reset(new OneSixFTBInstance(instDir, m_settings, this));
inst->setIntendedVersionId(version->descriptor());
inst->setShouldUseCustomBaseJar(false);
- break;
- default:
- {
- delete m_settings;
- return InstanceFactory::NoSuchVersion;
- }
}
}
else
diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp
index c0079d80..9170c87f 100644
--- a/logic/InstanceLauncher.cpp
+++ b/logic/InstanceLauncher.cpp
@@ -22,7 +22,7 @@
#include "gui/dialogs/ProgressDialog.h"
#include "logic/MinecraftProcess.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
InstanceLauncher::InstanceLauncher(QString instId) : QObject(), instId(instId)
{
diff --git a/logic/lists/InstanceList.cpp b/logic/InstanceList.cpp
index 1ff0d2ec..5a988fd3 100644
--- a/logic/lists/InstanceList.cpp
+++ b/logic/InstanceList.cpp
@@ -27,13 +27,13 @@
#include <pathutils.h>
#include "MultiMC.h"
-#include "logic/lists/InstanceList.h"
+#include "logic/InstanceList.h"
#include "logic/icons/IconList.h"
-#include "logic/lists/MinecraftVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
#include "logic/BaseInstance.h"
#include "logic/InstanceFactory.h"
#include "logger/QsLog.h"
-#include <gui/groupview/GroupView.h>
+#include "gui/groupview/GroupView.h"
const static int GROUP_FILE_FORMAT_VERSION = 1;
@@ -46,16 +46,6 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent)
{
QDir::current().mkpath(m_instDir);
}
-
- /*
- * FIXME HACK: instances sometimes need to be created at launch. They need the versions for
- * that.
- *
- * Remove this. it has no business of reloading the whole list. The instances which
- * need it should track such events themselves and CHANGE THEIR DATA ONLY!
- */
- connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this,
- &InstanceList::loadList);
}
InstanceList::~InstanceList()
@@ -296,17 +286,16 @@ void InstanceList::loadGroupList(QMap<QString, QString> &groupMap)
QList<FTBRecord> InstanceList::discoverFTBInstances()
{
QList<FTBRecord> records;
- QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString());
+ QDir dir = QDir(MMC->settings()->get("FTBLauncherDataRoot").toString());
QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString());
- if (!dir.exists())
+ if (!dataDir.exists())
{
- QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your "
- "settings.";
+ QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
return records;
}
- else if (!dataDir.exists())
+ else if (!dir.exists())
{
- QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings";
+ QLOG_INFO() << "The FTB launcher data directory specified does not exist. Please check your settings";
return records;
}
dir.cd("ModPacks");
@@ -337,6 +326,7 @@ QList<FTBRecord> InstanceList::discoverFTBInstances()
record.instanceDir = dataDir.absoluteFilePath(record.dirName);
record.templateDir = dir.absoluteFilePath(record.dirName);
QDir test(record.instanceDir);
+ QLOG_DEBUG() << dataDir.absolutePath() << record.instanceDir << record.dirName;
if (!test.exists())
continue;
record.name = attrs.value("name").toString();
diff --git a/logic/lists/InstanceList.h b/logic/InstanceList.h
index f0bbb7ec..f0bbb7ec 100644
--- a/logic/lists/InstanceList.h
+++ b/logic/InstanceList.h
diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp
index 3c3356c9..73a1f73d 100644
--- a/logic/LegacyFTBInstance.cpp
+++ b/logic/LegacyFTBInstance.cpp
@@ -14,11 +14,6 @@ QString LegacyFTBInstance::getStatusbarDescription()
return "Legacy FTB: " + intendedVersionId();
}
-bool LegacyFTBInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
-
QString LegacyFTBInstance::id() const
{
return "FTB/" + BaseInstance::id();
diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h
index 70f60535..a2fe1ead 100644
--- a/logic/LegacyFTBInstance.h
+++ b/logic/LegacyFTBInstance.h
@@ -9,6 +9,5 @@ public:
explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent = 0);
virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
virtual QString id() const;
};
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index 6648e059..5c15616a 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -28,8 +28,13 @@
#include "logic/MinecraftProcess.h"
#include "logic/LegacyUpdate.h"
#include "logic/icons/IconList.h"
-
-#include "gui/dialogs/LegacyModEditDialog.h"
+#include "gui/pages/LegacyUpgradePage.h"
+#include "gui/pages/ModFolderPage.h"
+#include "gui/pages/LegacyJarModPage.h"
+#include <gui/pages/TexturePackPage.h>
+#include <gui/pages/InstanceSettingsPage.h>
+#include <gui/pages/NotesPage.h>
+#include <gui/pages/ScreenshotsPage.h>
LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
QObject *parent)
@@ -42,6 +47,27 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
settings->registerSetting("IntendedJarVersion", "");
}
+QList<BasePage *> LegacyInstance::getPages()
+{
+ QList<BasePage *> values;
+ values.append(new LegacyUpgradePage(this));
+ values.append(new LegacyJarModPage(this));
+ values.append(new ModFolderPage(this, loaderModList(), "mods", "plugin-blue", tr("Loader mods"),
+ "Loader-mods"));
+ values.append(new ModFolderPage(this, coreModList(), "coremods", "plugin-green", tr("Core mods"),
+ "Core-mods"));
+ values.append(new TexturePackPage(this));
+ values.append(new NotesPage(this));
+ values.append(new ScreenshotsPage(this));
+ values.append(new InstanceSettingsPage(this));
+ return values;
+}
+
+QString LegacyInstance::dialogTitle()
+{
+ return tr("Edit Instance (%1)").arg(name());
+}
+
std::shared_ptr<Task> LegacyInstance::doUpdate()
{
// make sure the jar mods list is initialized by asking for it.
@@ -50,7 +76,7 @@ std::shared_ptr<Task> LegacyInstance::doUpdate()
return std::shared_ptr<Task>(new LegacyUpdate(this, this));
}
-bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString & launchScript)
+bool LegacyInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScript)
{
QIcon icon = MMC->icons()->getIcon(iconKey());
auto pixmap = icon.pixmap(128, 128);
@@ -136,11 +162,6 @@ std::shared_ptr<ModList> LegacyInstance::texturePackList()
return d->texture_pack_list;
}
-QDialog *LegacyInstance::createModEditDialog(QWidget *parent)
-{
- return new LegacyModEditDialog(this, parent);
-}
-
QString LegacyInstance::jarModsDir() const
{
return PathCombine(instanceRoot(), "instMods");
@@ -263,27 +284,11 @@ QString LegacyInstance::defaultCustomBaseJar() const
return PathCombine(binDir(), "mcbackup.jar");
}
-bool LegacyInstance::menuActionEnabled(QString action_name) const
-{
- if (flags().contains(VersionBrokenFlag))
- {
- return false;
- }
- if (action_name == "actionChangeInstMCVersion")
- {
- return false;
- }
- return true;
-}
-
QString LegacyInstance::getStatusbarDescription()
{
if (flags().contains(VersionBrokenFlag))
{
- return "Legacy : " + intendedVersionId() + " (broken)";
+ return tr("Legacy : %1 (broken)").arg(intendedVersionId());
}
- if (shouldUpdate())
- return "Legacy : " + currentVersionId() + " -> " + intendedVersionId();
- else
- return "Legacy : " + currentVersionId();
+ return tr("Legacy : %1").arg(intendedVersionId());
}
diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h
index aa80968e..76a8c24d 100644
--- a/logic/LegacyInstance.h
+++ b/logic/LegacyInstance.h
@@ -16,11 +16,12 @@
#pragma once
#include "BaseInstance.h"
+#include "gui/pages/BasePageProvider.h"
class ModList;
class Task;
-class LegacyInstance : public BaseInstance
+class LegacyInstance : public BaseInstance, public BasePageProvider
{
Q_OBJECT
public:
@@ -34,6 +35,10 @@ public:
//! Path to the instance's modlist file.
QString modListFile() const;
+ ////// Edit Instance Dialog stuff //////
+ virtual QList<BasePage *> getPages();
+ virtual QString dialogTitle();
+
////// Mod Lists //////
std::shared_ptr<ModList> jarModList();
std::shared_ptr<ModList> coreModList();
@@ -75,18 +80,21 @@ public:
return false;
}
+ virtual QSet<QString> traits()
+ {
+ return {"legacy-instance", "texturepacks"};
+ };
+
virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override;
virtual std::shared_ptr<Task> doUpdate() override;
virtual bool prepareForLaunch(AuthSessionPtr account, QString & launchScript) override;
virtual void cleanupAfterRun() override;
- virtual QDialog *createModEditDialog(QWidget *parent) override;
virtual QString defaultBaseJar() const override;
virtual QString defaultCustomBaseJar() const override;
- bool menuActionEnabled(QString action_name) const;
virtual QString getStatusbarDescription() override;
protected
diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp
index 15c99234..00ee795d 100644
--- a/logic/LegacyUpdate.cpp
+++ b/logic/LegacyUpdate.cpp
@@ -13,69 +13,27 @@
* limitations under the License.
*/
-#include "LegacyUpdate.h"
-#include "lists/LwjglVersionList.h"
-#include "lists/MinecraftVersionList.h"
-#include "BaseInstance.h"
-#include "LegacyInstance.h"
-#include "MultiMC.h"
-#include "ModList.h"
+#include <QStringList>
+
#include <pathutils.h>
#include <quazip.h>
#include <quazipfile.h>
#include <JlCompress.h>
+
+#include "logic/LegacyUpdate.h"
+#include "logic/LwjglVersionList.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/BaseInstance.h"
+#include "logic/LegacyInstance.h"
+#include "MultiMC.h"
+#include "logic/ModList.h"
+
#include "logger/QsLog.h"
#include "logic/net/URLConstants.h"
-#include <QStringList>
+
LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
- // 1.3 - 1.3.2
- auto libs13 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
-
- fmlLibsMapping["1.3.2"] = libs13;
-
- auto libs14 = QList<FMLlib>{
- {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
- {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
- {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
- {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
-
- fmlLibsMapping["1.4"] = libs14;
- fmlLibsMapping["1.4.1"] = libs14;
- fmlLibsMapping["1.4.2"] = libs14;
- fmlLibsMapping["1.4.3"] = libs14;
- fmlLibsMapping["1.4.4"] = libs14;
- fmlLibsMapping["1.4.5"] = libs14;
- fmlLibsMapping["1.4.6"] = libs14;
- fmlLibsMapping["1.4.7"] = libs14;
-
- fmlLibsMapping["1.5"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
-
- fmlLibsMapping["1.5.1"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
-
- fmlLibsMapping["1.5.2"] = QList<FMLlib>{
- {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
- {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
- {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
- {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
- {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
- {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
}
void LegacyUpdate::executeTask()
@@ -110,6 +68,7 @@ void LegacyUpdate::fmllibsStart()
bool forge_present = false;
QString version = inst->intendedVersionId();
+ auto & fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
if (!fmlLibsMapping.contains(version))
{
lwjglStart();
@@ -152,7 +111,7 @@ void LegacyUpdate::fmllibsStart()
// now check the lib folder inside the instance for files.
for (auto &lib : libList)
{
- QFileInfo libInfo(PathCombine(inst->libDir(), lib.name));
+ QFileInfo libInfo(PathCombine(inst->libDir(), lib.filename));
if (libInfo.exists())
continue;
fmlLibsToProcess.append(lib);
@@ -171,9 +130,9 @@ void LegacyUpdate::fmllibsStart()
auto metacache = MMC->metacache();
for (auto &lib : fmlLibsToProcess)
{
- auto entry = metacache->resolveEntry("fmllibs", lib.name);
- QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.name
- : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.name;
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
+ : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
dljob->addNetAction(CacheDownload::make(QUrl(urlString), entry));
}
@@ -196,16 +155,16 @@ void LegacyUpdate::fmllibsFinished()
for (auto &lib : fmlLibsToProcess)
{
progress(index, fmlLibsToProcess.size());
- auto entry = metacache->resolveEntry("fmllibs", lib.name);
- auto path = PathCombine(inst->libDir(), lib.name);
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ auto path = PathCombine(inst->libDir(), lib.filename);
if(!ensureFilePathExists(path))
{
emitFailed(tr("Failed creating FML library folder inside the instance."));
return;
}
- if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.name)))
+ if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.filename)))
{
- emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.name));
+ emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
return;
}
index++;
@@ -265,8 +224,6 @@ void LegacyUpdate::lwjglStart()
connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
connect(worker.get(), SIGNAL(finished(QNetworkReply *)),
SLOT(lwjglFinished(QNetworkReply *)));
- // connect(rep, SIGNAL(error(QNetworkReply::NetworkError)),
- // SLOT(downloadError(QNetworkReply::NetworkError)));
}
void LegacyUpdate::lwjglFinished(QNetworkReply *reply)
diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h
index 5b073cb7..140ee1e3 100644
--- a/logic/LegacyUpdate.h
+++ b/logic/LegacyUpdate.h
@@ -21,19 +21,13 @@
#include "logic/net/NetJob.h"
#include "logic/tasks/Task.h"
+#include "logic/VersionFilterData.h"
class MinecraftVersion;
class BaseInstance;
class QuaZip;
class Mod;
-struct FMLlib
-{
- QString name;
- QString checksum;
- bool ours;
-};
-
class LegacyUpdate : public Task
{
Q_OBJECT
@@ -84,5 +78,4 @@ private:
NetJobPtr legacyDownloadJob;
BaseInstance *m_inst = nullptr;
QList<FMLlib> fmlLibsToProcess;
- QMap<QString, QList<FMLlib>> fmlLibsMapping;
};
diff --git a/logic/lists/LwjglVersionList.cpp b/logic/LwjglVersionList.cpp
index df46d7be..df46d7be 100644
--- a/logic/lists/LwjglVersionList.cpp
+++ b/logic/LwjglVersionList.cpp
diff --git a/logic/lists/LwjglVersionList.h b/logic/LwjglVersionList.h
index fa57e8eb..fa57e8eb 100644
--- a/logic/lists/LwjglVersionList.h
+++ b/logic/LwjglVersionList.h
diff --git a/logic/MMCJson.cpp b/logic/MMCJson.cpp
index 65423436..8de88b6b 100644
--- a/logic/MMCJson.cpp
+++ b/logic/MMCJson.cpp
@@ -1,5 +1,6 @@
#include "MMCJson.h"
#include <QString>
+#include <QStringList>
#include <math.h>
bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
@@ -11,7 +12,7 @@ bool MMCJson::ensureBoolean(const QJsonValue val, const QString what)
QJsonValue MMCJson::ensureExists(QJsonValue val, const QString what)
{
- if(val.isNull())
+ if(val.isUndefined() || val.isUndefined())
throw JSONValidationError(what + " does not exist");
return val;
}
@@ -59,3 +60,24 @@ QString MMCJson::ensureString(const QJsonValue val, const QString what)
return val.toString();
}
+void MMCJson::writeString(QJsonObject &to, QString key, QString value)
+{
+ if(value.size())
+ {
+ to.insert(key, value);
+ }
+}
+
+void MMCJson::writeStringList(QJsonObject &to, QString key, QStringList values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value);
+ }
+ to.insert(key, array);
+ }
+}
+
diff --git a/logic/MMCJson.h b/logic/MMCJson.h
index 71ded435..8408f29b 100644
--- a/logic/MMCJson.h
+++ b/logic/MMCJson.h
@@ -43,4 +43,23 @@ 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");
+
+void writeString(QJsonObject & to, QString key, QString value);
+
+void writeStringList (QJsonObject & to, QString key, QStringList values);
+
+template <typename T>
+void writeObjectList (QJsonObject & to, QString key, QList<T> values)
+{
+ if(values.size())
+ {
+ QJsonArray array;
+ for(auto value: values)
+ {
+ array.append(value->toJson());
+ }
+ to.insert(key, array);
+ }
+}
}
+
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index b268a4cc..a3ffedba 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -71,6 +71,9 @@ MinecraftProcess::MinecraftProcess(InstancePtr inst) : m_instance(inst)
connect(&m_prepostlaunchprocess, &QProcess::readyReadStandardOutput, this,
&MinecraftProcess::on_prepost_stdOut);
}
+
+ // a process has been constructed for the instance. It is running from MultiMC POV
+ m_instance->setRunning(true);
}
void MinecraftProcess::setWorkdir(QString path)
@@ -254,6 +257,8 @@ void MinecraftProcess::finish(int code, ExitStatus status)
// run post-exit
postLaunch();
m_instance->cleanupAfterRun();
+ // no longer running...
+ m_instance->setRunning(false);
emit ended(m_instance, code, status);
}
@@ -304,6 +309,8 @@ bool MinecraftProcess::preLaunch()
m_instance->cleanupAfterRun();
emit prelaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
m_prepostlaunchprocess.exitStatus());
+ // not running, failed
+ m_instance->setRunning(false);
return false;
}
else
@@ -343,6 +350,8 @@ bool MinecraftProcess::postLaunch()
MessageLevel::Error);
emit postlaunch_failed(m_instance, m_prepostlaunchprocess.exitCode(),
m_prepostlaunchprocess.exitStatus());
+ // not running, failed
+ m_instance->setRunning(false);
}
else
emit log(tr("Post-Launch command ran successfully.\n\n"));
@@ -460,6 +469,8 @@ void MinecraftProcess::arm()
emit log(tr("Could not launch minecraft!"), MessageLevel::Error);
m_instance->cleanupAfterRun();
emit launch_failed(m_instance);
+ // not running, failed
+ m_instance->setRunning(false);
return;
}
// send the launch script to the launcher part
diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h
deleted file mode 100644
index 504381a8..00000000
--- a/logic/MinecraftVersion.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/* Copyright 2013 Andrew Okin
- *
- * 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 "BaseVersion.h"
-#include <QStringList>
-
-struct MinecraftVersion : public BaseVersion
-{
- /*!
- * Gets the version's timestamp.
- * This is primarily used for sorting versions in a list.
- */
- qint64 timestamp;
-
- /// The URL that this version will be downloaded from. maybe.
- QString download_url;
-
- /// This version's type. Used internally to identify what kind of version this is.
- enum VersionType
- {
- OneSix,
- Legacy,
- Nostalgia
- } type;
-
- /// is this the latest version?
- bool is_latest = false;
-
- /// is this a snapshot?
- bool is_snapshot = false;
-
- QString m_name;
-
- QString m_descriptor;
-
- virtual QString descriptor()
- {
- return m_descriptor;
- }
-
- virtual QString name()
- {
- return m_name;
- }
-
- virtual QString typeString() const
- {
- QStringList pre_final;
- if (is_latest == true)
- {
- pre_final.append("Latest");
- }
- switch (type)
- {
- case OneSix:
- pre_final.append("OneSix");
- break;
- case Legacy:
- pre_final.append("Legacy");
- break;
- case Nostalgia:
- pre_final.append("Nostalgia");
- break;
-
- default:
- pre_final.append(QString("Type(%1)").arg(type));
- break;
- }
- if (is_snapshot == true)
- {
- pre_final.append("Snapshot");
- }
- return pre_final.join(' ');
- }
-};
diff --git a/logic/ModList.cpp b/logic/ModList.cpp
index 79b56986..f7770920 100644
--- a/logic/ModList.cpp
+++ b/logic/ModList.cpp
@@ -26,6 +26,7 @@
ModList::ModList(const QString &dir, const QString &list_file)
: QAbstractListModel(), m_dir(dir), m_list_file(list_file)
{
+ ensureFolderPathExists(m_dir.absolutePath());
m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs |
QDir::NoSymLinks);
m_dir.setSorting(QDir::Name | QDir::IgnoreCase | QDir::LocaleAware);
diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp
deleted file mode 100644
index 52820725..00000000
--- a/logic/NostalgiaInstance.cpp
+++ /dev/null
@@ -1,36 +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 "NostalgiaInstance.h"
-
-NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
- QObject *parent)
- : OneSixInstance(rootDir, settings, parent)
-{
-}
-
-QString NostalgiaInstance::getStatusbarDescription()
-{
- if (flags().contains(VersionBrokenFlag))
- {
- return "Nostalgia : " + intendedVersionId() + " (broken)";
- }
- return "Nostalgia : " + intendedVersionId();
-}
-
-bool NostalgiaInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h
deleted file mode 100644
index f95531d2..00000000
--- a/logic/NostalgiaInstance.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "OneSixInstance.h"
-
-class NostalgiaInstance : public OneSixInstance
-{
- Q_OBJECT
-public:
- explicit NostalgiaInstance(const QString &rootDir, SettingsObject *settings,
- QObject *parent = 0);
- virtual ~NostalgiaInstance() {};
- virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
-};
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 172830bb..ef951987 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -1,12 +1,12 @@
#include "OneSixFTBInstance.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/minecraft/VersionBuilder.h"
#include "tasks/SequentialTask.h"
-#include "ForgeInstaller.h"
-#include "lists/ForgeVersionList.h"
+#include "forge/ForgeInstaller.h"
+#include "forge/ForgeVersionList.h"
#include "OneSixInstance_p.h"
-#include "OneSixVersionBuilder.h"
#include "MultiMC.h"
#include "pathutils.h"
@@ -100,6 +100,7 @@ QDir OneSixFTBInstance::librariesPath() const
{
return QDir(MMC->settings()->get("FTBRoot").toString() + "/libraries");
}
+
QDir OneSixFTBInstance::versionsPath() const
{
return QDir(MMC->settings()->get("FTBRoot").toString() + "/versions");
@@ -107,7 +108,6 @@ QDir OneSixFTBInstance::versionsPath() const
QStringList OneSixFTBInstance::externalPatches() const
{
- I_D(OneSixInstance);
return QStringList() << versionsPath().absoluteFilePath(intendedVersionId() + "/" + intendedVersionId() + ".json")
<< minecraftRoot() + "/pack.json";
}
@@ -125,10 +125,6 @@ QString OneSixFTBInstance::getStatusbarDescription()
}
return "OneSix FTB: " + intendedVersionId();
}
-bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
-{
- return false;
-}
std::shared_ptr<Task> OneSixFTBInstance::doUpdate()
{
diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h
index 440dc9f1..ecfa2231 100644
--- a/logic/OneSixFTBInstance.h
+++ b/logic/OneSixFTBInstance.h
@@ -16,7 +16,6 @@ public:
void copy(const QDir &newDir) override;
virtual QString getStatusbarDescription();
- virtual bool menuActionEnabled(QString action_name) const;
virtual std::shared_ptr<Task> doUpdate() override;
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 6f3018cb..7bac3424 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -13,52 +13,84 @@
* limitations under the License.
*/
-#include "OneSixInstance.h"
-
#include <QIcon>
-
-#include "OneSixInstance_p.h"
-#include "OneSixUpdate.h"
-#include "VersionFinal.h"
-#include "pathutils.h"
+#include <pathutils.h>
#include "logger/QsLog.h"
-#include "assets/AssetsUtils.h"
#include "MultiMC.h"
-#include "icons/IconList.h"
-#include "MinecraftProcess.h"
-#include "gui/dialogs/OneSixModEditDialog.h"
-#include <MMCError.h>
+#include "MMCError.h"
+
+#include "logic/OneSixInstance.h"
-OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings, QObject *parent)
+#include "logic/OneSixInstance_p.h"
+#include "logic/OneSixUpdate.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "minecraft/VersionBuildError.h"
+
+#include "logic/assets/AssetsUtils.h"
+#include "icons/IconList.h"
+#include "logic/MinecraftProcess.h"
+#include "gui/pagedialog/PageDialog.h"
+#include "gui/pages/VersionPage.h"
+#include <gui/pages/ModFolderPage.h>
+#include <gui/pages/ResourcePackPage.h>
+#include <gui/pages/TexturePackPage.h>
+#include <gui/pages/InstanceSettingsPage.h>
+#include <gui/pages/NotesPage.h>
+#include <gui/pages/ScreenshotsPage.h>
+
+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);
- d->version.reset(new VersionFinal(this, this));
- d->vanillaVersion.reset(new VersionFinal(this, this));
+ d->version.reset(new InstanceVersion(this, this));
}
void OneSixInstance::init()
{
- // FIXME: why is this decided here? what does this even mean?
- if (QDir(instanceRoot()).exists("version.json"))
+ try
{
- try
- {
- reloadVersion();
- }
- catch(MMCError & e)
- {
- // QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
- }
+ reloadVersion();
}
- else
+ catch (MMCError &e)
{
- clearVersion();
+ QLOG_ERROR() << "Caught exception on instance init: " << e.cause();
}
}
+QList<BasePage *> OneSixInstance::getPages()
+{
+ QList<BasePage *> values;
+ values.append(new VersionPage(this));
+ values.append(new ModFolderPage(this, loaderModList(), "mods", "plugin-blue", tr("Loader mods"),
+ "Loader-mods"));
+ values.append(new ModFolderPage(this, coreModList(), "coremods", "plugin-green", tr("Core mods"),
+ "Core-mods"));
+ values.append(new ResourcePackPage(this));
+ values.append(new TexturePackPage(this));
+ values.append(new NotesPage(this));
+ values.append(new ScreenshotsPage(this));
+ values.append(new InstanceSettingsPage(this));
+ return values;
+}
+
+QString OneSixInstance::dialogTitle()
+{
+ return tr("Edit Instance (%1)").arg(name());
+}
+
+QSet<QString> OneSixInstance::traits()
+{
+ auto version = getFullVersion();
+ if (!version)
+ {
+ return {"version-incomplete"};
+ }
+ else
+ return version->traits;
+}
+
std::shared_ptr<Task> OneSixInstance::doUpdate()
{
return std::shared_ptr<Task>(new OneSixUpdate(this));
@@ -88,7 +120,7 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with)
return result;
}
-QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
+QDir OneSixInstance::reconstructAssets(std::shared_ptr<InstanceVersion> version)
{
QDir assetsDir = QDir("assets/");
QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes"));
@@ -126,7 +158,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
QString original_path =
PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash);
QFile original(original_path);
- if(!original.exists())
+ if (!original.exists())
continue;
if (!target.exists())
{
@@ -138,7 +170,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<VersionFinal> version)
bool couldCopy = original.copy(target_path);
QLOG_DEBUG() << " Copying" << original_path << "to" << target_path
- << QString::number(couldCopy); // << original.errorString();
+ << QString::number(couldCopy); // << original.errorString();
}
}
@@ -189,7 +221,7 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
return parts;
}
-bool OneSixInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScript)
+bool OneSixInstance::prepareForLaunch(AuthSessionPtr session, QString &launchScript)
{
I_D(OneSixInstance);
@@ -200,44 +232,79 @@ bool OneSixInstance::prepareForLaunch(AuthSessionPtr account, QString &launchScr
auto version = d->version;
if (!version)
return nullptr;
+
+ // libraries and class path.
{
auto libs = version->getActiveNormalLibs();
for (auto lib : libs)
{
launchScript += "cp " + librariesPath().absoluteFilePath(lib->storagePath()) + "\n";
}
- QString targetstr = version->id + "/" + version->id + ".jar";
- launchScript += "cp " + versionsPath().absoluteFilePath(targetstr) + "\n";
+ QString minecraftjarpath;
+ if (version->hasJarMods())
+ {
+ for (auto jarmod : version->jarMods)
+ {
+ launchScript += "cp " + jarmodsPath().absoluteFilePath(jarmod->name) + "\n";
+ }
+ minecraftjarpath = version->id + "/" + version->id + "-stripped.jar";
+ }
+ else
+ {
+ minecraftjarpath = version->id + "/" + version->id + ".jar";
+ }
+ launchScript += "cp " + versionsPath().absoluteFilePath(minecraftjarpath) + "\n";
+ }
+ if (!version->mainClass.isEmpty())
+ {
+ launchScript += "mainClass " + version->mainClass + "\n";
+ }
+ if (!version->appletClass.isEmpty())
+ {
+ launchScript += "appletClass " + version->appletClass + "\n";
}
- launchScript += "mainClass " + version->mainClass + "\n";
- for (auto param : processMinecraftArgs(account))
+ // generic minecraft params
+ for (auto param : processMinecraftArgs(session))
{
launchScript += "param " + param + "\n";
}
- // Set the width and height for 1.6 instances
- bool maximize = settings().get("LaunchMaximized").toBool();
- if (maximize)
+ // window size, title and state, legacy
{
- // this is probably a BAD idea
- // launchScript += "param --fullscreen\n";
+ QString windowParams;
+ if (settings().get("LaunchMaximized").toBool())
+ windowParams = "max";
+ else
+ windowParams = QString("%1x%2")
+ .arg(settings().get("MinecraftWinWidth").toInt())
+ .arg(settings().get("MinecraftWinHeight").toInt());
+ launchScript += "windowTitle " + windowTitle() + "\n";
+ launchScript += "windowParams " + windowParams + "\n";
}
- else
+
+ // legacy auth
+ {
+ launchScript += "userName " + session->player_name + "\n";
+ launchScript += "sessionId " + session->session + "\n";
+ }
+
+ // native libraries (mostly LWJGL)
{
- launchScript +=
- "param --width\nparam " + settings().get("MinecraftWinWidth").toString() + "\n";
- launchScript +=
- "param --height\nparam " + settings().get("MinecraftWinHeight").toString() + "\n";
+ QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
+ for (auto native : version->getActiveNativeLibs())
+ {
+ QFileInfo finfo(PathCombine("libraries", native->storagePath()));
+ launchScript += "ext " + finfo.absoluteFilePath() + "\n";
+ }
+ launchScript += "natives " + natives_dir.absolutePath() + "\n";
}
- QDir natives_dir(PathCombine(instanceRoot(), "natives/"));
- launchScript += "windowTitle " + windowTitle() + "\n";
- for(auto native: version->getActiveNativeLibs())
+
+ // traits. including legacyLaunch and others ;)
+ for (auto trait : version->traits)
{
- QFileInfo finfo(PathCombine("libraries", native->storagePath()));
- launchScript += "ext " + finfo.absoluteFilePath() + "\n";
+ launchScript += "traits " + trait + "\n";
}
- launchScript += "natives " + natives_dir.absolutePath() + "\n";
launchScript += "launcher onesix\n";
return true;
}
@@ -260,6 +327,17 @@ std::shared_ptr<ModList> OneSixInstance::loaderModList()
return d->loader_mod_list;
}
+std::shared_ptr<ModList> OneSixInstance::coreModList()
+{
+ I_D(OneSixInstance);
+ if (!d->core_mod_list)
+ {
+ d->core_mod_list.reset(new ModList(coreModsDir()));
+ }
+ d->core_mod_list->update();
+ return d->core_mod_list;
+}
+
std::shared_ptr<ModList> OneSixInstance::resourcePackList()
{
I_D(OneSixInstance);
@@ -271,15 +349,20 @@ std::shared_ptr<ModList> OneSixInstance::resourcePackList()
return d->resource_pack_list;
}
-QDialog *OneSixInstance::createModEditDialog(QWidget *parent)
+std::shared_ptr<ModList> OneSixInstance::texturePackList()
{
- return new OneSixModEditDialog(this, parent);
+ I_D(OneSixInstance);
+ if (!d->texture_pack_list)
+ {
+ d->texture_pack_list.reset(new ModList(texturePacksDir()));
+ }
+ d->texture_pack_list->update();
+ return d->texture_pack_list;
}
bool OneSixInstance::setIntendedVersionId(QString version)
{
settings().set("IntendedVersion", version);
- setShouldUpdate(true);
QFile::remove(PathCombine(instanceRoot(), "version.json"));
clearVersion();
return true;
@@ -290,34 +373,31 @@ QString OneSixInstance::intendedVersionId() const
return settings().get("IntendedVersion").toString();
}
-void OneSixInstance::setShouldUpdate(bool val)
+void OneSixInstance::setShouldUpdate(bool)
{
- settings().set("ShouldUpdate", val);
}
bool OneSixInstance::shouldUpdate() const
{
- QVariant var = settings().get("ShouldUpdate");
- if (!var.isValid() || var.toBool() == false)
- {
- return intendedVersionId() != currentVersionId();
- }
return true;
}
bool OneSixInstance::versionIsCustom()
{
- QDir patches(PathCombine(instanceRoot(), "patches/"));
- return (patches.exists() && patches.count() >= 0)
- || QFile::exists(PathCombine(instanceRoot(), "custom.json"))
- || QFile::exists(PathCombine(instanceRoot(), "user.json"));
+ I_D(const OneSixInstance);
+ auto ver = d->version;
+ if (ver)
+ {
+ return !ver->isVanilla();
+ }
+ return false;
}
bool OneSixInstance::versionIsFTBPack()
{
I_D(const OneSixInstance);
auto ver = d->version;
- if(ver)
+ if (ver)
{
return ver->hasFtbPack();
}
@@ -335,17 +415,18 @@ void OneSixInstance::reloadVersion()
try
{
- d->version->reload(false, externalPatches());
- d->vanillaVersion->reload(true, externalPatches());
+ d->version->reload(externalPatches());
d->m_flags.remove(VersionBrokenFlag);
emit versionReloaded();
}
- catch(MMCError & error)
+ catch (VersionIncomplete &error)
+ {
+ }
+ catch (MMCError &error)
{
d->version->clear();
- d->vanillaVersion->clear();
d->m_flags.insert(VersionBrokenFlag);
- //TODO: rethrow to show some error message(s)?
+ // TODO: rethrow to show some error message(s)?
emit versionReloaded();
throw;
}
@@ -355,22 +436,15 @@ void OneSixInstance::clearVersion()
{
I_D(OneSixInstance);
d->version->clear();
- d->vanillaVersion->clear();
emit versionReloaded();
}
-std::shared_ptr<VersionFinal> OneSixInstance::getFullVersion() const
+std::shared_ptr<InstanceVersion> OneSixInstance::getFullVersion() const
{
I_D(const OneSixInstance);
return d->version;
}
-std::shared_ptr<VersionFinal> OneSixInstance::getVanillaVersion() const
-{
- I_D(const OneSixInstance);
- return d->vanillaVersion;
-}
-
QString OneSixInstance::defaultBaseJar() const
{
return "versions/" + intendedVersionId() + "/" + intendedVersionId() + ".jar";
@@ -381,37 +455,38 @@ QString OneSixInstance::defaultCustomBaseJar() const
return PathCombine(instanceRoot(), "custom.jar");
}
-bool OneSixInstance::menuActionEnabled(QString action_name) const
+QString OneSixInstance::getStatusbarDescription()
{
- if (flags().contains(VersionBrokenFlag))
+ QStringList traits;
+ if (versionIsCustom())
{
- return false;
+ traits.append(tr("custom"));
}
- if (action_name == "actionChangeInstLWJGLVersion")
+ if (flags().contains(VersionBrokenFlag))
{
- return false;
+ traits.append(tr("broken"));
}
- return true;
-}
-QString OneSixInstance::getStatusbarDescription()
-{
- QString descr = "OneSix : " + intendedVersionId();
- if (versionIsCustom())
+ if (traits.size())
{
- descr += " (custom)";
+ return tr("Minecraft %1 (%2)").arg(intendedVersionId()).arg(traits.join(", "));
}
- if (flags().contains(VersionBrokenFlag))
+ else
{
- descr += " (broken)";
+ return tr("Minecraft %1").arg(intendedVersionId());
}
- return descr;
}
QDir OneSixInstance::librariesPath() const
{
return QDir::current().absoluteFilePath("libraries");
}
+
+QDir OneSixInstance::jarmodsPath() const
+{
+ return QDir(jarModsDir());
+}
+
QDir OneSixInstance::versionsPath() const
{
return QDir::current().absoluteFilePath("versions");
@@ -429,7 +504,7 @@ bool OneSixInstance::providesVersionFile() const
bool OneSixInstance::reload()
{
- if(BaseInstance::reload())
+ if (BaseInstance::reload())
{
try
{
@@ -449,12 +524,46 @@ QString OneSixInstance::loaderModsDir() const
return PathCombine(minecraftRoot(), "mods");
}
+QString OneSixInstance::coreModsDir() const
+{
+ return PathCombine(minecraftRoot(), "coremods");
+}
+
QString OneSixInstance::resourcePacksDir() const
{
return PathCombine(minecraftRoot(), "resourcepacks");
}
+QString OneSixInstance::texturePacksDir() const
+{
+ return PathCombine(minecraftRoot(), "texturepacks");
+}
+
QString OneSixInstance::instanceConfigFolder() const
{
return PathCombine(minecraftRoot(), "config");
}
+
+QString OneSixInstance::jarModsDir() const
+{
+ return PathCombine(instanceRoot(), "jarmods");
+}
+
+QString OneSixInstance::libDir() const
+{
+ return PathCombine(minecraftRoot(), "lib");
+}
+
+QStringList OneSixInstance::extraArguments() const
+{
+ auto list = BaseInstance::extraArguments();
+ auto version = getFullVersion();
+ if (!version)
+ return list;
+ if (version->hasJarMods())
+ {
+ list.append({"-Dfml.ignoreInvalidMinecraftCertificates=true",
+ "-Dfml.ignorePatchDiscrepancies=true"});
+ }
+ return list;
+}
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index 13a392c2..75caef1f 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -17,10 +17,11 @@
#include "BaseInstance.h"
-#include "VersionFinal.h"
-#include "ModList.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/ModList.h"
+#include "gui/pages/BasePageProvider.h"
-class OneSixInstance : public BaseInstance
+class OneSixInstance : public BaseInstance, public BasePageProvider
{
Q_OBJECT
public:
@@ -30,13 +31,25 @@ public:
virtual void init() override;
+ ////// Edit Instance Dialog stuff //////
+ virtual QList<BasePage *> getPages();
+ virtual QString dialogTitle();
+
////// Mod Lists //////
std::shared_ptr<ModList> loaderModList();
- std::shared_ptr<ModList> resourcePackList();
-
- ////// Directories //////
+ std::shared_ptr<ModList> coreModList();
+ std::shared_ptr<ModList> resourcePackList() override;
+ std::shared_ptr<ModList> texturePackList() override;
+
+ virtual QSet<QString> traits();
+
+ ////// Directories and files //////
+ QString jarModsDir() const;
QString resourcePacksDir() const;
+ QString texturePacksDir() const;
QString loaderModsDir() const;
+ QString coreModsDir() const;
+ QString libDir() const;
virtual QString instanceConfigFolder() const override;
virtual std::shared_ptr<Task> doUpdate() override;
@@ -52,42 +65,43 @@ public:
virtual bool shouldUpdate() const override;
virtual void setShouldUpdate(bool val) override;
- virtual QDialog *createModEditDialog(QWidget *parent) override;
-
/**
- * reload the full version json files. return true on success!
+ * reload the full version json files.
*
* 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<VersionFinal> getFullVersion() const;
- /// gets the current version info, but only for version.json
- std::shared_ptr<VersionFinal> getVanillaVersion() const;
+ std::shared_ptr<InstanceVersion> getFullVersion() const;
+
/// is the current version original, or custom?
virtual bool versionIsCustom() override;
+
/// does this instance have an FTB pack patch inside?
bool versionIsFTBPack();
virtual QString defaultBaseJar() const override;
virtual QString defaultCustomBaseJar() const override;
- virtual bool menuActionEnabled(QString action_name) const override;
virtual QString getStatusbarDescription() override;
+ virtual QDir jarmodsPath() const;
virtual QDir librariesPath() const;
virtual QDir versionsPath() const;
virtual QStringList externalPatches() const;
virtual bool providesVersionFile() const;
bool reload() override;
-
+ virtual QStringList extraArguments() const override;
+
signals:
void versionReloaded();
private:
QStringList processMinecraftArgs(AuthSessionPtr account);
- QDir reconstructAssets(std::shared_ptr<VersionFinal> version);
+ QDir reconstructAssets(std::shared_ptr<InstanceVersion> version);
};
diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h
index c70de07c..3c4ef324 100644
--- a/logic/OneSixInstance_p.h
+++ b/logic/OneSixInstance_p.h
@@ -15,16 +15,19 @@
#pragma once
-#include "BaseInstance_p.h"
-#include "VersionFinal.h"
-#include "ModList.h"
+#include "logic/BaseInstance_p.h"
+
+class ModList;
+class InstanceVersion;
class OneSixInstancePrivate : public BaseInstancePrivate
{
public:
virtual ~OneSixInstancePrivate() {};
- std::shared_ptr<VersionFinal> version;
- std::shared_ptr<VersionFinal> vanillaVersion;
+ std::shared_ptr<InstanceVersion> version;
+ std::shared_ptr<ModList> jar_mod_list;
std::shared_ptr<ModList> loader_mod_list;
+ std::shared_ptr<ModList> core_mod_list;
std::shared_ptr<ModList> resource_pack_list;
+ std::shared_ptr<ModList> texture_pack_list;
};
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index d083c2ba..abc5f9c1 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -22,28 +22,24 @@
#include <QFileInfo>
#include <QTextStream>
#include <QDataStream>
-
-#include "BaseInstance.h"
-#include "lists/MinecraftVersionList.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
-#include "net/ForgeMirrors.h"
-#include "net/URLConstants.h"
-#include "assets/AssetsUtils.h"
-
-#include "pathutils.h"
+#include <pathutils.h>
#include <JlCompress.h>
-OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent)
- : Task(parent), m_inst(inst)
+#include "logic/BaseInstance.h"
+#include "logic/minecraft/MinecraftVersionList.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/OneSixInstance.h"
+#include "logic/forge/ForgeMirrors.h"
+#include "logic/net/URLConstants.h"
+#include "logic/assets/AssetsUtils.h"
+
+OneSixUpdate::OneSixUpdate(OneSixInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
{
}
void OneSixUpdate::executeTask()
{
- QString intendedVersion = m_inst->intendedVersionId();
-
// Make directories
QDir mcDir(m_inst->minecraftRoot());
if (!mcDir.exists() && !mcDir.mkpath("."))
@@ -52,102 +48,44 @@ void OneSixUpdate::executeTask()
return;
}
- if (m_inst->shouldUpdate())
+ // Get a pointer to the version object that corresponds to the instance's version.
+ targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
+ MMC->minecraftlist()->findVersion(m_inst->intendedVersionId()));
+ if (targetVersion == nullptr)
{
- // Get a pointer to the version object that corresponds to the instance's version.
- targetVersion = std::dynamic_pointer_cast<MinecraftVersion>(
- MMC->minecraftlist()->findVersion(intendedVersion));
- if (targetVersion == nullptr)
- {
- // don't do anything if it was invalid
- emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
- return;
- }
- versionFileStart();
+ // don't do anything if it was invalid
+ emitFailed(tr("The specified Minecraft version is invalid. Choose a different one."));
+ return;
}
- else
+ if (m_inst->providesVersionFile() || !targetVersion->needsUpdate())
{
jarlibStart();
+ return;
}
-}
-
-void OneSixUpdate::versionFileStart()
-{
- if (m_inst->providesVersionFile())
+ versionUpdateTask = MMC->minecraftlist()->createUpdateTask(m_inst->intendedVersionId());
+ if (!versionUpdateTask)
{
jarlibStart();
return;
}
- QLOG_INFO() << m_inst->name() << ": getting version file.";
- setStatus(tr("Getting the version files from Mojang..."));
-
- QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS +
- targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json";
- auto job = new NetJob("Version index");
- job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
- specificVersionDownloadJob.reset(job);
- connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished()));
- connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed()));
- connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ connect(versionUpdateTask.get(), SIGNAL(succeeded()), SLOT(jarlibStart()));
+ connect(versionUpdateTask.get(), SIGNAL(failed(QString)), SLOT(versionUpdateFailed(QString)));
+ connect(versionUpdateTask.get(), SIGNAL(progress(qint64, qint64)),
SIGNAL(progress(qint64, qint64)));
- specificVersionDownloadJob->start();
-}
-
-void OneSixUpdate::versionFileFinished()
-{
- NetActionPtr DlJob = specificVersionDownloadJob->first();
-
- QString version_id = targetVersion->descriptor();
- QString inst_dir = m_inst->instanceRoot();
- // save the version file in $instanceId/version.json
- {
- QString version1 = PathCombine(inst_dir, "/version.json");
- ensureFilePathExists(version1);
- // FIXME: detect errors here, download to a temp file, swap
- QSaveFile vfile1(version1);
- if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
- {
- 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(tr("Failed to write into %1. Written %2 out of %3.").arg(version1).arg(actual).arg(data.size()));
- return;
- }
- if (!vfile1.commit())
- {
- emitFailed(tr("Can't commit changes to %1").arg(version1));
- return;
- }
- }
-
- // the version is downloaded safely. update is 'done' at this point
- m_inst->setShouldUpdate(false);
-
- // delete any custom version inside the instance (it's no longer relevant, we did an update)
- QString custom = PathCombine(inst_dir, "/custom.json");
- QFile finfo(custom);
- if (finfo.exists())
- {
- finfo.remove();
- }
- // NOTE: Version is reloaded in jarlibStart
- jarlibStart();
+ setStatus(tr("Getting the version files from Mojang..."));
+ versionUpdateTask->start();
}
-void OneSixUpdate::versionFileFailed()
+void OneSixUpdate::versionUpdateFailed(QString reason)
{
- emitFailed(tr("Failed to download the version description. Try again."));
+ emitFailed(reason);
}
void OneSixUpdate::assetIndexStart()
{
setStatus(tr("Updating assets index..."));
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
QString assetName = version->assets;
QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json";
QString localPath = assetName + ".json";
@@ -171,7 +109,7 @@ void OneSixUpdate::assetIndexFinished()
AssetsIndex index;
OneSixInstance *inst = (OneSixInstance *)m_inst;
- std::shared_ptr<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
QString assetName = version->assets;
QString asset_fname = "assets/indexes/" + assetName + ".json";
@@ -235,19 +173,19 @@ void OneSixUpdate::jarlibStart()
{
inst->reloadVersion();
}
- catch(MMCError & e)
+ catch (MMCError &e)
{
emitFailed(e.cause());
return;
}
- catch(...)
+ catch (...)
{
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<VersionFinal> version = inst->getFullVersion();
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
// minecraft.jar for this version
{
QString version_id = version->id;
@@ -259,7 +197,7 @@ void OneSixUpdate::jarlibStart()
auto metacache = MMC->metacache();
auto entry = metacache->resolveEntry("versions", localPath);
job->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
-
+ jarHashOnEntry = entry->md5sum;
jarlibDownloadJob.reset(job);
}
@@ -274,7 +212,7 @@ void OneSixUpdate::jarlibStart()
{
if (lib->hint() == "local")
{
- if(!lib->filesExist(m_inst->librariesPath()))
+ if (!lib->filesExist(m_inst->librariesPath()))
brokenLocalLibs.append(lib);
continue;
}
@@ -311,16 +249,19 @@ void OneSixUpdate::jarlibStart()
f(raw_storage, raw_dl);
}
}
- if(!brokenLocalLibs.empty())
+ if (!brokenLocalLibs.empty())
{
jarlibDownloadJob.reset();
QStringList failed;
- for(auto brokenLib : brokenLocalLibs)
+ for (auto brokenLib : brokenLocalLibs)
{
failed.append(brokenLib->files());
}
QString failed_all = failed.join("\n");
- emitFailed(tr("Some libraries marked as 'local' are missing their jar files:\n%1\n\nYou'll have to correct this problem manually. If this is an externally tracked instance, make sure to run it at least once outside of MultiMC.").arg(failed_all));
+ emitFailed(tr("Some libraries marked as 'local' are missing their jar "
+ "files:\n%1\n\nYou'll have to correct this problem manually. If this is "
+ "an externally tracked instance, make sure to run it at least once "
+ "outside of MultiMC.").arg(failed_all));
return;
}
// TODO: think about how to propagate this from the original json file... or IF AT ALL
@@ -341,12 +282,222 @@ void OneSixUpdate::jarlibStart()
void OneSixUpdate::jarlibFinished()
{
- assetIndexStart();
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<InstanceVersion> version = inst->getFullVersion();
+
+ // create stripped jar, if needed
+ if (version->hasJarMods())
+ {
+ // FIXME: good candidate for moving elsewhere (jar location resolving/version caching).
+ QString version_id = version->id;
+ QString localPath = version_id + "/" + version_id + ".jar";
+ QString strippedPath = version_id + "/" + version_id + "-stripped.jar";
+ auto metacache = MMC->metacache();
+ auto entry = metacache->resolveEntry("versions", localPath);
+ auto entryStripped = metacache->resolveEntry("versions", strippedPath);
+
+ QString fullJarPath = entry->getFullPath();
+ QString fullStrippedJarPath = entryStripped->getFullPath();
+ QFileInfo finfo(fullStrippedJarPath);
+ if (entry->md5sum != jarHashOnEntry || !finfo.exists())
+ {
+ stripJar(fullJarPath, fullStrippedJarPath);
+ }
+ }
+ if (version->traits.contains("legacyFML"))
+ {
+ fmllibsStart();
+ }
+ else
+ {
+ assetIndexStart();
+ }
}
void OneSixUpdate::jarlibFailed()
{
QStringList failed = jarlibDownloadJob->getFailedFiles();
QString failed_all = failed.join("\n");
- emitFailed(tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
+ emitFailed(
+ tr("Failed to download the following files:\n%1\n\nPlease try again.").arg(failed_all));
+}
+
+void OneSixUpdate::stripJar(QString origPath, QString newPath)
+{
+ QFileInfo runnableJar(newPath);
+ if (runnableJar.exists() && !QFile::remove(runnableJar.filePath()))
+ {
+ emitFailed("Failed to delete old minecraft.jar");
+ return;
+ }
+
+ // TaskStep(); // STEP 1
+ setStatus(tr("Creating stripped jar: Opening minecraft.jar ..."));
+
+ QuaZip zipOut(runnableJar.filePath());
+ if (!zipOut.open(QuaZip::mdCreate))
+ {
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to open the minecraft.jar for stripping");
+ return;
+ }
+ // Modify the jar
+ setStatus(tr("Creating stripped jar: Adding files..."));
+ if (!MergeZipFiles(&zipOut, origPath))
+ {
+ zipOut.close();
+ QFile::remove(runnableJar.filePath());
+ emitFailed("Failed to add " + origPath + " to the jar.");
+ return;
+ }
+}
+
+bool OneSixUpdate::MergeZipFiles(QuaZip *into, QString from)
+{
+ setStatus(tr("Installing mods: Adding ") + from + " ...");
+
+ QuaZip modZip(from);
+ modZip.open(QuaZip::mdUnzip);
+
+ QuaZipFile fileInsideMod(&modZip);
+ QuaZipFile zipOutFile(into);
+ for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
+ {
+ QString filename = modZip.getCurrentFileName();
+ if (filename.contains("META-INF"))
+ {
+ QLOG_INFO() << "Skipping META-INF " << filename << " from " << from;
+ continue;
+ }
+ QLOG_INFO() << "Adding file " << filename << " from " << from;
+
+ if (!fileInsideMod.open(QIODevice::ReadOnly))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " from " << from;
+ return false;
+ }
+ /*
+ QuaZipFileInfo old_info;
+ fileInsideMod.getFileInfo(&old_info);
+ */
+ QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
+ /*
+ info_out.externalAttr = old_info.externalAttr;
+ */
+ if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
+ {
+ QLOG_ERROR() << "Failed to open " << filename << " in the jar";
+ fileInsideMod.close();
+ return false;
+ }
+ if (!JlCompress::copyData(fileInsideMod, zipOutFile))
+ {
+ zipOutFile.close();
+ fileInsideMod.close();
+ QLOG_ERROR() << "Failed to copy data of " << filename << " into the jar";
+ return false;
+ }
+ zipOutFile.close();
+ fileInsideMod.close();
+ }
+ return true;
+}
+
+void OneSixUpdate::fmllibsStart()
+{
+ // Get the mod list
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ std::shared_ptr<InstanceVersion> fullversion = inst->getFullVersion();
+ bool forge_present = false;
+
+ QString version = inst->intendedVersionId();
+ auto &fmlLibsMapping = g_VersionFilterData.fmlLibsMapping;
+ if (!fmlLibsMapping.contains(version))
+ {
+ assetIndexStart();
+ return;
+ }
+
+ auto &libList = fmlLibsMapping[version];
+
+ // determine if we need some libs for FML or forge
+ setStatus(tr("Checking for FML libraries..."));
+ forge_present = (fullversion->versionPatch("net.minecraftforge") != nullptr);
+ // we don't...
+ if (!forge_present)
+ {
+ assetIndexStart();
+ return;
+ }
+
+ // now check the lib folder inside the instance for files.
+ for (auto &lib : libList)
+ {
+ QFileInfo libInfo(PathCombine(inst->libDir(), lib.filename));
+ if (libInfo.exists())
+ continue;
+ fmlLibsToProcess.append(lib);
+ }
+
+ // if everything is in place, there's nothing to do here...
+ if (fmlLibsToProcess.isEmpty())
+ {
+ assetIndexStart();
+ return;
+ }
+
+ // download missing libs to our place
+ setStatus(tr("Dowloading FML libraries..."));
+ auto dljob = new NetJob("FML libraries");
+ auto metacache = MMC->metacache();
+ for (auto &lib : fmlLibsToProcess)
+ {
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ QString urlString = lib.ours ? URLConstants::FMLLIBS_OUR_BASE_URL + lib.filename
+ : URLConstants::FMLLIBS_FORGE_BASE_URL + lib.filename;
+ dljob->addNetAction(CacheDownload::make(QUrl(urlString), entry));
+ }
+
+ connect(dljob, SIGNAL(succeeded()), SLOT(fmllibsFinished()));
+ connect(dljob, SIGNAL(failed()), SLOT(fmllibsFailed()));
+ connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64)));
+ legacyDownloadJob.reset(dljob);
+ legacyDownloadJob->start();
+}
+
+void OneSixUpdate::fmllibsFinished()
+{
+ legacyDownloadJob.reset();
+ if (!fmlLibsToProcess.isEmpty())
+ {
+ setStatus(tr("Copying FML libraries into the instance..."));
+ OneSixInstance *inst = (OneSixInstance *)m_inst;
+ auto metacache = MMC->metacache();
+ int index = 0;
+ for (auto &lib : fmlLibsToProcess)
+ {
+ progress(index, fmlLibsToProcess.size());
+ auto entry = metacache->resolveEntry("fmllibs", lib.filename);
+ auto path = PathCombine(inst->libDir(), lib.filename);
+ if (!ensureFilePathExists(path))
+ {
+ emitFailed(tr("Failed creating FML library folder inside the instance."));
+ return;
+ }
+ if (!QFile::copy(entry->getFullPath(), PathCombine(inst->libDir(), lib.filename)))
+ {
+ emitFailed(tr("Failed copying Forge/FML library: %1.").arg(lib.filename));
+ return;
+ }
+ index++;
+ }
+ progress(index, fmlLibsToProcess.size());
+ }
+ assetIndexStart();
+}
+
+void OneSixUpdate::fmllibsFailed()
+{
+ emitFailed("Game update failed: it was impossible to fetch the required FML libraries.");
+ return;
}
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
index eac882b5..139143db 100644
--- a/logic/OneSixUpdate.h
+++ b/logic/OneSixUpdate.h
@@ -21,6 +21,8 @@
#include "logic/net/NetJob.h"
#include "logic/tasks/Task.h"
+#include "logic/VersionFilterData.h"
+#include <quazip.h>
class MinecraftVersion;
class OneSixInstance;
@@ -34,14 +36,16 @@ public:
private
slots:
- void versionFileStart();
- void versionFileFinished();
- void versionFileFailed();
+ void versionUpdateFailed(QString reason);
void jarlibStart();
void jarlibFinished();
void jarlibFailed();
+ void fmllibsStart();
+ void fmllibsFinished();
+ void fmllibsFailed();
+
void assetIndexStart();
void assetIndexFinished();
void assetIndexFailed();
@@ -49,11 +53,18 @@ slots:
void assetsFinished();
void assetsFailed();
+ void stripJar(QString origPath, QString newPath);
+ bool MergeZipFiles(QuaZip *into, QString from);
private:
- NetJobPtr specificVersionDownloadJob;
NetJobPtr jarlibDownloadJob;
+ NetJobPtr legacyDownloadJob;
- // target version, determined during this task
+ /// target version, determined during this task
std::shared_ptr<MinecraftVersion> targetVersion;
+ /// the task that is spawned for version updates
+ std::shared_ptr<Task> versionUpdateTask;
+
OneSixInstance *m_inst = nullptr;
+ QString jarHashOnEntry;
+ QList<FMLlib> fmlLibsToProcess;
};
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
deleted file mode 100644
index be3a7da4..00000000
--- a/logic/OneSixVersionBuilder.cpp
+++ /dev/null
@@ -1,252 +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 "OneSixVersionBuilder.h"
-
-#include <QList>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QFile>
-#include <QFileInfo>
-#include <QMessageBox>
-#include <QObject>
-#include <QDir>
-#include <QDebug>
-
-#include "VersionFinal.h"
-#include "OneSixInstance.h"
-#include "OneSixRule.h"
-#include "VersionFile.h"
-#include "MMCJson.h"
-#include "modutils.h"
-#include "logger/QsLog.h"
-
-OneSixVersionBuilder::OneSixVersionBuilder()
-{
-}
-
-void OneSixVersionBuilder::build(VersionFinal *version, OneSixInstance *instance,
- const bool onlyVanilla, const QStringList &external)
-{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = instance;
- builder.buildInternal(onlyVanilla, external);
-}
-
-void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
- const QJsonObject &obj)
-{
- OneSixVersionBuilder builder;
- builder.m_version = version;
- builder.m_instance = 0;
- builder.readJsonAndApply(obj);
-}
-
-void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
-{
- m_version->versionFiles.clear();
-
- QDir root(m_instance->instanceRoot());
- QDir patches(root.absoluteFilePath("patches/"));
-
- // if we do external files, do just those.
- if (!external.isEmpty())
- {
- int externalOrder = -1;
- 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->order = (externalOrder += 1);
- file->version = QString();
- file->mcVersion = QString();
- m_version->versionFiles.append(file);
- }
- }
- // else, if there's custom json, we just do that.
- else if (QFile::exists(root.absoluteFilePath("custom.json")))
- {
- QLOG_INFO() << "Reading custom.json";
- auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("custom.json")), false);
- file->name = "custom.json";
- file->filename = "custom.json";
- file->fileId = "org.multimc.custom.json";
- file->order = -1;
- file->version = QString();
- 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";
- auto file = parseJsonFile(QFileInfo(root.absoluteFilePath("version.json")), false);
- file->name = "Minecraft";
- file->fileId = "org.multimc.version.json";
- file->order = -1;
- file->version = m_instance->intendedVersionId();
- file->mcVersion = m_instance->intendedVersionId();
- 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<int, QPair<QString, VersionFilePtr>> files;
- for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
- {
- 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));
- }
- for (auto order : files.keys())
- {
- auto &filePair = files[order];
- m_version->versionFiles.append(filePair.second);
- }
- } while (0);
-
- // some final touches
- m_version->finalize();
-}
-
-
-
-void OneSixVersionBuilder::readJsonAndApply(const QJsonObject &obj)
-{
- m_version->clear();
-
- auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
- // QObject::tr("Error while reading. Please check MultiMC-0.log for more info."));
-
- 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"));
-}
-
-VersionFilePtr OneSixVersionBuilder::parseJsonFile(const QFileInfo &fileInfo,
- const bool requireOrder, bool isFTB)
-{
- QFile file(fileInfo.absoluteFilePath());
- if (!file.open(QFile::ReadOnly))
- {
- 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)
- {
- throw JSONValidationError(QObject::tr("Unable to process the version file %1: %2 at %3.")
- .arg(fileInfo.fileName(), error.errorString())
- .arg(error.offset));
- }
- 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;
-
- // 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))
- {
- 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)
- {
- if (it.key().startsWith("org.multimc."))
- {
- 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)
-{
- QJsonObject obj;
- for (auto it = order.cbegin(); it != order.cend(); ++it)
- {
- if (it.key().startsWith("org.multimc."))
- {
- continue;
- }
- obj.insert(it.key(), it.value());
- }
- QFile orderFile(instance->instanceRoot() + "/order.json");
- if (!orderFile.open(QFile::WriteOnly))
- {
- QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
- << "for writing:" << orderFile.errorString();
- return false;
- }
- orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
- return true;
-}
diff --git a/logic/OneSixVersionBuilder.h b/logic/OneSixVersionBuilder.h
deleted file mode 100644
index 7a799e5b..00000000
--- a/logic/OneSixVersionBuilder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2013 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <QString>
-#include <QMap>
-#include "VersionFile.h"
-
-class VersionFinal;
-class OneSixInstance;
-class QJsonObject;
-class QFileInfo;
-
-class OneSixVersionBuilder
-{
- OneSixVersionBuilder();
-public:
- 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);
-
-private:
- VersionFinal *m_version;
- OneSixInstance *m_instance;
-
- void buildInternal(const bool onlyVanilla, const QStringList &external);
- void readJsonAndApply(const QJsonObject &obj);
-
- VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
- bool isFTB = false);
-};
diff --git a/logic/URNResolver.cpp b/logic/URNResolver.cpp
new file mode 100644
index 00000000..b6bdcf41
--- /dev/null
+++ b/logic/URNResolver.cpp
@@ -0,0 +1,98 @@
+#include "URNResolver.h"
+#include <logger/QsLog.h>
+#include "MultiMC.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/forge/ForgeVersion.h"
+
+QString unescapeNSS(QString RawNSS)
+{
+ QString NSS;
+ NSS.reserve(RawNSS.size());
+ enum
+ {
+ Normal,
+ FirstHex,
+ SecondHex
+ } ParseState = Normal;
+
+ QByteArray translator(" ");
+
+ for (auto ch : RawNSS)
+ {
+ if(ParseState == Normal)
+ {
+ if(ch == '%')
+ {
+ ParseState = FirstHex;
+ continue;
+ }
+ else
+ {
+ NSS.append(ch);
+ }
+ }
+ if(ParseState == FirstHex)
+ {
+ translator[0] = ch.toLower().unicode();
+ ParseState = SecondHex;
+ }
+ else if(ParseState == SecondHex)
+ {
+ translator[1] = ch.toLower().unicode();
+ auto result = QByteArray::fromHex(translator);
+ if (result[0] == '\0')
+ return NSS;
+ NSS.append(result);
+ ParseState = Normal;
+ }
+ }
+ return NSS;
+}
+
+bool URNResolver::parse(const QString &URN, QString &NID, QString &NSS)
+{
+ QRegExp URNPattern(
+ "^urn:([a-z0-9][a-z0-9-]{0,31}):(([a-z0-9()+,\\-.:=@;$_!*']|%[0-9a-f]{2})+).*",
+ Qt::CaseInsensitive);
+ if (URNPattern.indexIn(URN) == -1)
+ return false;
+ auto captures = URNPattern.capturedTexts();
+ QString RawNID = captures[1];
+ QString RawNSS = captures[2];
+
+ NID = RawNID.toLower();
+ NSS = unescapeNSS(RawNSS);
+ return true;
+}
+
+URNResolver::URNResolver()
+{
+}
+
+QVariant URNResolver::resolve(QString URN)
+{
+ QString NID, NSS;
+ parse(URN, NID, NSS);
+
+ if(NID != "x-mmc")
+ return QVariant();
+ auto parts = NSS.split(":");
+ if(parts.size() < 1)
+ return QVariant();
+ unsigned int version = parts[0].toUInt();
+ switch(version)
+ {
+ case 1:
+ return resolveV1(parts.mid(1));
+ default:
+ return QVariant();
+ }
+}
+
+/**
+ * TODO: implement.
+ */
+QVariant URNResolver::resolveV1(QStringList parts)
+{
+ return QVariant();
+}
diff --git a/logic/URNResolver.h b/logic/URNResolver.h
new file mode 100644
index 00000000..dfc7f5eb
--- /dev/null
+++ b/logic/URNResolver.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QString>
+#include <QMap>
+#include <memory>
+#include <QVariant>
+
+class URNResolver;
+typedef std::shared_ptr<URNResolver> URNResolverPtr;
+
+class URNResolver
+{
+public:
+ URNResolver();
+ QVariant resolve (QString URN);
+ static bool parse (const QString &URN, QString &NID, QString &NSS);
+private:
+ QVariant resolveV1 (QStringList parts);
+};
diff --git a/logic/VersionFile.h b/logic/VersionFile.h
deleted file mode 100644
index 169a2066..00000000
--- a/logic/VersionFile.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#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/VersionFilterData.cpp b/logic/VersionFilterData.cpp
new file mode 100644
index 00000000..8b521266
--- /dev/null
+++ b/logic/VersionFilterData.cpp
@@ -0,0 +1,68 @@
+#include "VersionFilterData.h"
+#include "minecraft/ParseUtils.h"
+
+extern VersionFilterData g_VersionFilterData = VersionFilterData();
+
+VersionFilterData::VersionFilterData()
+{
+ // 1.3.*
+ auto libs13 =
+ QList<FMLlib>{{"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false}};
+
+ fmlLibsMapping["1.3.2"] = libs13;
+
+ // 1.4.*
+ auto libs14 = QList<FMLlib>{
+ {"argo-2.25.jar", "bb672829fde76cb163004752b86b0484bd0a7f4b", false},
+ {"guava-12.0.1.jar", "b8e78b9af7bf45900e14c6f958486b6ca682195f", false},
+ {"asm-all-4.0.jar", "98308890597acb64047f7e896638e0d98753ae82", false},
+ {"bcprov-jdk15on-147.jar", "b6f5d9926b0afbde9f4dbe3db88c5247be7794bb", false}};
+
+ fmlLibsMapping["1.4"] = libs14;
+ fmlLibsMapping["1.4.1"] = libs14;
+ fmlLibsMapping["1.4.2"] = libs14;
+ fmlLibsMapping["1.4.3"] = libs14;
+ fmlLibsMapping["1.4.4"] = libs14;
+ fmlLibsMapping["1.4.5"] = libs14;
+ fmlLibsMapping["1.4.6"] = libs14;
+ fmlLibsMapping["1.4.7"] = libs14;
+
+ // 1.5
+ fmlLibsMapping["1.5"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.zip", "5f7c142d53776f16304c0bbe10542014abad6af8", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // 1.5.1
+ fmlLibsMapping["1.5.1"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.1.zip", "22e221a0d89516c1f721d6cab056a7e37471d0a6", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // 1.5.2
+ fmlLibsMapping["1.5.2"] = QList<FMLlib>{
+ {"argo-small-3.2.jar", "58912ea2858d168c50781f956fa5b59f0f7c6b51", false},
+ {"guava-14.0-rc3.jar", "931ae21fa8014c3ce686aaa621eae565fefb1a6a", false},
+ {"asm-all-4.1.jar", "054986e962b88d8660ae4566475658469595ef58", false},
+ {"bcprov-jdk15on-148.jar", "960dea7c9181ba0b17e8bab0c06a43f0a5f04e65", true},
+ {"deobfuscation_data_1.5.2.zip", "446e55cd986582c70fcf12cb27bc00114c5adfd9", false},
+ {"scala-library.jar", "458d046151ad179c85429ed7420ffb1eaf6ddf85", true}};
+
+ // don't use installers for those.
+ forgeInstallerBlacklist = QSet<QString>({"1.5.2"});
+ // these won't show up in version lists because they are extremely bad and dangerous
+ legacyBlacklist = QSet<QString>({"rd-160052"});
+ /*
+ * nothing older than this will be accepted from Mojang servers
+ * (these versions need to be tested by us first)
+ */
+ legacyCutoffDate = timeFromS3Time("2013-06-25T15:08:56+02:00");
+}
diff --git a/logic/VersionFilterData.h b/logic/VersionFilterData.h
new file mode 100644
index 00000000..e010adc7
--- /dev/null
+++ b/logic/VersionFilterData.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <QMap>
+#include <QString>
+#include <QSet>
+#include <QDateTime>
+
+struct FMLlib
+{
+ QString filename;
+ QString checksum;
+ bool ours;
+};
+
+struct VersionFilterData
+{
+ VersionFilterData();
+ // mapping between minecraft versions and FML libraries required
+ QMap<QString, QList<FMLlib>> fmlLibsMapping;
+ // set of minecraft versions for which using forge installers is blacklisted
+ QSet<QString> forgeInstallerBlacklist;
+ // set of 'legacy' versions that will not show up in the version lists.
+ QSet<QString> legacyBlacklist;
+ // no new versions below this date will be accepted from Mojang servers
+ QDateTime legacyCutoffDate;
+};
+extern VersionFilterData g_VersionFilterData;
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
deleted file mode 100644
index dedf2ce5..00000000
--- a/logic/VersionFinal.cpp
+++ /dev/null
@@ -1,377 +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 "VersionFinal.h"
-
-#include <QDebug>
-#include <QFile>
-#include <QDir>
-
-#include "OneSixVersionBuilder.h"
-#include "OneSixInstance.h"
-
-template <typename A, typename B> QMap<A, B> invert(const QMap<B, A> &in)
-{
- QMap<A, B> out;
- for (auto it = in.begin(); it != in.end(); ++it)
- {
- out.insert(it.value(), it.key());
- }
- return out;
-}
-
-VersionFinal::VersionFinal(OneSixInstance *instance, QObject *parent)
- : QAbstractListModel(parent), m_instance(instance)
-{
- clear();
-}
-
-void VersionFinal::reload(const bool onlyVanilla, const QStringList &external)
-{
- //FIXME: source of epic failure.
- beginResetModel();
- OneSixVersionBuilder::build(this, m_instance, onlyVanilla, external);
- reapply(true);
- endResetModel();
-}
-
-void VersionFinal::clear()
-{
- id.clear();
- time.clear();
- releaseTime.clear();
- type.clear();
- assets.clear();
- processArguments.clear();
- minecraftArguments.clear();
- minimumLauncherVersion = 0xDEADBEAF;
- mainClass.clear();
- libraries.clear();
- tweakers.clear();
-}
-
-bool VersionFinal::canRemove(const int index) const
-{
- if (index < versionFiles.size())
- {
- return versionFiles.at(index)->fileId != "org.multimc.version.json";
- }
- return false;
-}
-
-bool VersionFinal::remove(const int index)
-{
- if (canRemove(index) && QFile::remove(versionFiles.at(index)->filename))
- {
- beginResetModel();
- versionFiles.removeAt(index);
- reapply(true);
- endResetModel();
- return true;
- }
- return false;
-}
-
-bool VersionFinal::remove(const QString id)
-{
- int i = 0;
- for (auto file : versionFiles)
- {
- if (file->fileId == id)
- {
- return remove(i);
- }
- i++;
- }
- return false;
-}
-
-QString VersionFinal::versionFileId(const int index) const
-{
- if (index < 0 || index >= versionFiles.size())
- {
- return QString();
- }
- return versionFiles.at(index)->fileId;
-}
-
-VersionFilePtr VersionFinal::versionFile(const QString &id)
-{
- for (auto file : versionFiles)
- {
- if (file->fileId == id)
- {
- return file;
- }
- }
- return 0;
-}
-
-bool VersionFinal::hasFtbPack()
-{
- return versionFile("org.multimc.ftb.pack.json") != nullptr;
-}
-
-bool VersionFinal::removeFtbPack()
-{
- return remove("org.multimc.ftb.pack.json");
-}
-
-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;
-}
-
-bool VersionFinal::isCustom()
-{
- return QDir(m_instance->instanceRoot()).exists("custom.json");
-}
-bool VersionFinal::revertToBase()
-{
- return QDir(m_instance->instanceRoot()).remove("custom.json");
-}
-
-QMap<QString, int> VersionFinal::getExistingOrder() const
-{
-
- QMap<QString, int> order;
- // default
- {
- for (auto file : versionFiles)
- {
- order.insert(file->fileId, file->order);
- }
- }
- // overriden
- {
- QMap<QString, int> overridenOrder = OneSixVersionBuilder::readOverrideOrders(m_instance);
- for (auto id : order.keys())
- {
- if (overridenOrder.contains(id))
- {
- order[id] = overridenOrder[id];
- }
- }
- }
- return order;
-}
-
-void VersionFinal::move(const int index, const MoveDirection direction)
-{
- int theirIndex;
- if (direction == MoveUp)
- {
- theirIndex = index - 1;
- }
- else
- {
- theirIndex = index + 1;
- }
- if (theirIndex < 0 || theirIndex >= versionFiles.size())
- {
- return;
- }
- const QString ourId = versionFileId(index);
- const QString theirId = versionFileId(theirIndex);
- if (ourId.isNull() || ourId.startsWith("org.multimc.") ||
- theirId.isNull() || theirId.startsWith("org.multimc."))
- {
- return;
- }
-
- VersionFilePtr we = versionFiles[index];
- VersionFilePtr them = versionFiles[theirIndex];
- if (!we || !them)
- {
- return;
- }
- beginMoveRows(QModelIndex(), index, index, QModelIndex(), theirIndex);
- versionFiles.replace(theirIndex, we);
- versionFiles.replace(index, them);
- endMoveRows();
-
- auto order = getExistingOrder();
- order[ourId] = theirIndex;
- order[theirId] = index;
-
- if (!OneSixVersionBuilder::writeOverrideOrders(order, m_instance))
- {
- throw MMCError(tr("Couldn't save the new order"));
- }
- else
- {
- reapply();
- }
-}
-void VersionFinal::resetOrder()
-{
- QDir(m_instance->instanceRoot()).remove("order.json");
- reapply();
-}
-
-void VersionFinal::reapply(const bool alreadyReseting)
-{
- if (!alreadyReseting)
- {
- beginResetModel();
- }
-
- clear();
-
- auto existingOrders = getExistingOrder();
- QList<int> orders = existingOrders.values();
- std::sort(orders.begin(), orders.end());
- QList<VersionFilePtr> newVersionFiles;
- for (auto order : orders)
- {
- auto file = versionFile(existingOrders.key(order));
- newVersionFiles.append(file);
- file->applyTo(this);
- }
- versionFiles.swap(newVersionFiles);
- finalize();
- if (!alreadyReseting)
- {
- endResetModel();
- }
-}
-
-void VersionFinal::finalize()
-{
- // HACK: deny april fools. my head hurts enough already.
- QDate now = QDate::currentDate();
- bool isAprilFools = now.month() == 4 && now.day() == 1;
- if (assets.endsWith("_af") && !isAprilFools)
- {
- assets = assets.left(assets.length() - 3);
- }
- if (assets.isEmpty())
- {
- assets = "legacy";
- }
- if (minecraftArguments.isEmpty())
- {
- QString toCompare = processArguments.toLower();
- if (toCompare == "legacy")
- {
- minecraftArguments = " ${auth_player_name} ${auth_session}";
- }
- else if (toCompare == "username_session")
- {
- minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
- }
- else if (toCompare == "username_session_version")
- {
- minecraftArguments = "--username ${auth_player_name} "
- "--session ${auth_session} "
- "--version ${profile_name}";
- }
- }
-}
diff --git a/logic/ForgeInstaller.cpp b/logic/forge/ForgeInstaller.cpp
index 94b3f319..7f952408 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/forge/ForgeInstaller.cpp
@@ -14,9 +14,15 @@
*/
#include "ForgeInstaller.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "net/HttpMetaCache.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/net/HttpMetaCache.h"
+#include "logic/tasks/Task.h"
+#include "logic/OneSixInstance.h"
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/VersionFilterData.h"
+#include "gui/dialogs/ProgressDialog.h"
+
#include <quazip.h>
#include <quazipfile.h>
#include <pathutils.h>
@@ -24,23 +30,17 @@
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include "MultiMC.h"
-#include "tasks/Task.h"
-#include "OneSixInstance.h"
-#include "lists/ForgeVersionList.h"
-#include "gui/dialogs/ProgressDialog.h"
-
#include <QJsonDocument>
#include <QJsonArray>
#include <QSaveFile>
#include <QCryptographicHash>
-ForgeInstaller::ForgeInstaller()
- : BaseInstaller()
+ForgeInstaller::ForgeInstaller() : BaseInstaller()
{
}
void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
{
- std::shared_ptr<VersionFinal> newVersion;
+ std::shared_ptr<InstanceVersion> newVersion;
m_universal_url = universalUrl;
QuaZip zip(filename);
@@ -73,7 +73,7 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
// read the forge version info
{
- newVersion = VersionFinal::fromJson(versionInfoVal.toObject());
+ newVersion = InstanceVersion::fromJson(versionInfoVal.toObject());
if (!newVersion)
return;
}
@@ -115,8 +115,8 @@ void ForgeInstaller::prepare(const QString &filename, const QString &universalUr
}
file.close();
- m_forge_version = newVersion;
- realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
+ m_forge_json = newVersion;
+ realVersionId = m_forge_json->id = installObj.value("minecraft").toString();
}
bool ForgeInstaller::add(OneSixInstance *to)
{
@@ -128,7 +128,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
QJsonObject obj;
obj.insert("order", 5);
- if (!m_forge_version)
+ if (!m_forge_json)
return false;
int sliding_insert_window = 0;
{
@@ -136,7 +136,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
// 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)
+ for (auto lib : m_forge_json->libraries)
{
QString libName = lib->name();
// WARNING: This could actually break.
@@ -157,7 +157,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
bool found = false;
bool equals = false;
// find an entry that matches this one
- for (auto tolib : to->getVanillaVersion()->libraries)
+ for (auto tolib : to->getFullVersion()->vanillaLibraries)
{
if (tolib->name() != libName)
continue;
@@ -187,8 +187,8 @@ bool ForgeInstaller::add(OneSixInstance *to)
librariesPlus.prepend(libObj);
}
obj.insert("+libraries", librariesPlus);
- obj.insert("mainClass", m_forge_version->mainClass);
- QString args = m_forge_version->minecraftArguments;
+ obj.insert("mainClass", m_forge_json->mainClass);
+ QString args = m_forge_json->minecraftArguments;
QStringList tweakers;
{
QRegularExpression expression("--tweakClass ([a-zA-Z0-9\\.]*)");
@@ -200,7 +200,7 @@ bool ForgeInstaller::add(OneSixInstance *to)
match = expression.match(args);
}
}
- if (!args.isEmpty() && args != to->getVanillaVersion()->minecraftArguments)
+ if (!args.isEmpty() && args != to->getFullVersion()->vanillaMinecraftArguments)
{
obj.insert("minecraftArguments", args);
}
@@ -208,10 +208,10 @@ bool ForgeInstaller::add(OneSixInstance *to)
{
obj.insert("+tweakers", QJsonArray::fromStringList(tweakers));
}
- if (!m_forge_version->processArguments.isEmpty() &&
- m_forge_version->processArguments != to->getVanillaVersion()->processArguments)
+ if (!m_forge_json->processArguments.isEmpty() &&
+ m_forge_json->processArguments != to->getFullVersion()->vanillaProcessArguments)
{
- obj.insert("processArguments", m_forge_version->processArguments);
+ obj.insert("processArguments", m_forge_json->processArguments);
}
}
@@ -233,11 +233,65 @@ bool ForgeInstaller::add(OneSixInstance *to)
return true;
}
+bool ForgeInstaller::addLegacy(OneSixInstance *to)
+{
+ if (!BaseInstaller::add(to))
+ {
+ return false;
+ }
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", m_forge_version->filename());
+ finalPath = PathCombine(to->jarModsDir(), m_forge_version->filename());
+ if (!ensureFilePathExists(finalPath))
+ {
+ return false;
+ }
+ if (!QFile::copy(entry->getFullPath(),finalPath))
+ {
+ return false;
+ }
+ QJsonObject obj;
+ obj.insert("order", 5);
+ {
+ QJsonArray jarmodsPlus;
+ {
+ QJsonObject libObj;
+ libObj.insert("name", m_forge_version->universal_filename);
+ jarmodsPlus.append(libObj);
+ }
+ obj.insert("+jarMods", jarmodsPlus);
+ }
+
+ obj.insert("name", QString("Forge"));
+ obj.insert("fileId", id());
+ obj.insert("version", m_forge_version->jobbuildver);
+ obj.insert("mcVersion", to->intendedVersionId());
+ if (g_VersionFilterData.fmlLibsMapping.contains(m_forge_version->mcver))
+ {
+ QJsonArray traitsPlus;
+ traitsPlus.append(QString("legacyFML"));
+ obj.insert("+traits", traitsPlus);
+ }
+ auto fullversion = to->getFullVersion();
+ fullversion->remove("net.minecraftforge");
+
+ 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;
+}
+
class ForgeInstallTask : public Task
{
Q_OBJECT
public:
- ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, BaseVersionPtr version, QObject *parent = 0)
+ ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance,
+ BaseVersionPtr version, QObject *parent = 0)
: Task(parent), m_installer(installer), m_instance(instance), m_version(version)
{
}
@@ -245,57 +299,59 @@ public:
protected:
void executeTask() override
{
+ setStatus(tr("Installing forge..."));
+ ForgeVersionPtr forgeVersion = std::dynamic_pointer_cast<ForgeVersion>(m_version);
+ if (!forgeVersion)
{
- setStatus(tr("Installing forge..."));
- ForgeVersionPtr forgeVersion =
- std::dynamic_pointer_cast<ForgeVersion>(m_version);
- if (!forgeVersion)
- {
- emitFailed(tr("Unknown error occured"));
- return;
- }
- auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
- if (entry->stale)
+ emitFailed(tr("Unknown error occured"));
+ return;
+ }
+ prepare(forgeVersion);
+ }
+ void prepare(ForgeVersionPtr forgeVersion)
+ {
+ auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename());
+ auto installFunction = [this, entry, forgeVersion]()
+ {
+ if (!install(entry, forgeVersion))
{
- NetJob *fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
- connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total){setProgress(100 * current / qMax((qint64)1, total));});
- connect(fjob, &NetJob::status, [this](const QString &msg){setStatus(msg);});
- connect(fjob, &NetJob::failed, [this](){emitFailed(tr("Failure to download forge"));});
- connect(fjob, &NetJob::succeeded, [this, entry, forgeVersion]()
- {
- if (!install(entry, forgeVersion))
- {
- QLOG_ERROR() << "Failure installing forge";
- emitFailed(tr("Failure to install forge"));
- }
- else
- {
- reload();
- }
- });
- fjob->start();
+ QLOG_ERROR() << "Failure installing forge";
+ emitFailed(tr("Failure to install forge"));
}
else
{
- if (!install(entry, forgeVersion))
- {
- QLOG_ERROR() << "Failure installing forge";
- emitFailed(tr("Failure to install forge"));
- }
- else
- {
- reload();
- }
+ reload();
}
+ };
+
+ if (entry->stale)
+ {
+ NetJob *fjob = new NetJob("Forge download");
+ fjob->addNetAction(CacheDownload::make(forgeVersion->url(), entry));
+ connect(fjob, &NetJob::progress, [this](qint64 current, qint64 total)
+ { setProgress(100 * current / qMax((qint64)1, total)); });
+ connect(fjob, &NetJob::status, [this](const QString & msg)
+ { setStatus(msg); });
+ connect(fjob, &NetJob::failed, [this]()
+ { emitFailed(tr("Failure to download forge")); });
+ connect(fjob, &NetJob::succeeded, installFunction);
+ fjob->start();
+ }
+ else
+ {
+ installFunction();
}
}
-
bool install(const std::shared_ptr<MetaEntry> &entry, const ForgeVersionPtr &forgeVersion)
{
- QString forgePath = entry->getFullPath();
- m_installer->prepare(forgePath, forgeVersion->universal_url);
- return m_installer->add(m_instance);
+ if (forgeVersion->usesInstaller())
+ {
+ QString forgePath = entry->getFullPath();
+ m_installer->prepare(forgePath, forgeVersion->universal_url);
+ return m_installer->add(m_instance);
+ }
+ else
+ return m_installer->addLegacy(m_instance);
}
void reload()
{
@@ -320,8 +376,14 @@ private:
BaseVersionPtr m_version;
};
-ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
+ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance,
+ BaseVersionPtr version, QObject *parent)
{
+ if (!version)
+ {
+ return nullptr;
+ }
+ m_forge_version = std::dynamic_pointer_cast<ForgeVersion>(version);
return new ForgeInstallTask(this, instance, version, parent);
}
diff --git a/logic/ForgeInstaller.h b/logic/forge/ForgeInstaller.h
index 05cc994b..14aeeb51 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/forge/ForgeInstaller.h
@@ -15,28 +15,34 @@
#pragma once
-#include "BaseInstaller.h"
+#include "logic/BaseInstaller.h"
#include <QString>
#include <memory>
-class VersionFinal;
+class InstanceVersion;
+class ForgeInstallTask;
+class ForgeVersion;
class ForgeInstaller : public BaseInstaller
{
+ friend class ForgeInstallTask;
public:
ForgeInstaller();
+ virtual ~ForgeInstaller(){};
+ virtual ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+protected:
+ virtual QString id() const override { return "net.minecraftforge"; }
void prepare(const QString &filename, const QString &universalUrl);
bool add(OneSixInstance *to) override;
-
- QString id() const override { return "net.minecraftforge"; }
-
- ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+ bool addLegacy(OneSixInstance *to);
private:
- // the version, read from the installer
- std::shared_ptr<VersionFinal> m_forge_version;
+ // the parsed version json, read from the installer
+ std::shared_ptr<InstanceVersion> m_forge_json;
+ // the actual forge version
+ std::shared_ptr<ForgeVersion> m_forge_version;
QString internalPath;
QString finalPath;
QString realVersionId;
diff --git a/logic/net/ForgeMirror.h b/logic/forge/ForgeMirror.h
index 2518dffe..2518dffe 100644
--- a/logic/net/ForgeMirror.h
+++ b/logic/forge/ForgeMirror.h
diff --git a/logic/net/ForgeMirrors.cpp b/logic/forge/ForgeMirrors.cpp
index b224306f..b224306f 100644
--- a/logic/net/ForgeMirrors.cpp
+++ b/logic/forge/ForgeMirrors.cpp
diff --git a/logic/net/ForgeMirrors.h b/logic/forge/ForgeMirrors.h
index 6784fba1..d25762db 100644
--- a/logic/net/ForgeMirrors.h
+++ b/logic/forge/ForgeMirrors.h
@@ -15,10 +15,10 @@
#pragma once
-#include "NetAction.h"
-#include "HttpMetaCache.h"
-#include "ForgeXzDownload.h"
-#include "NetJob.h"
+#include "logic/net/NetAction.h"
+#include "logic/net/HttpMetaCache.h"
+#include "logic/net/NetJob.h"
+#include "logic/forge/ForgeXzDownload.h"
#include <QFile>
#include <QTemporaryFile>
typedef std::shared_ptr<class ForgeMirrors> ForgeMirrorsPtr;
diff --git a/logic/forge/ForgeVersion.cpp b/logic/forge/ForgeVersion.cpp
new file mode 100644
index 00000000..3131ec39
--- /dev/null
+++ b/logic/forge/ForgeVersion.cpp
@@ -0,0 +1,55 @@
+#include "ForgeVersion.h"
+#include "logic/VersionFilterData.h"
+#include <QObject>
+
+QString ForgeVersion::name()
+{
+ return "Forge " + jobbuildver;
+}
+
+QString ForgeVersion::descriptor()
+{
+ return universal_filename;
+}
+
+QString ForgeVersion::typeString() const
+{
+ if (is_recommended)
+ return QObject::tr("Recommended");
+ return QString();
+}
+
+bool ForgeVersion::operator<(BaseVersion &a)
+{
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if (!pa)
+ return true;
+ return m_buildnr < pa->m_buildnr;
+}
+
+bool ForgeVersion::operator>(BaseVersion &a)
+{
+ ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
+ if (!pa)
+ return false;
+ return m_buildnr > pa->m_buildnr;
+}
+
+bool ForgeVersion::usesInstaller()
+{
+ if(installer_url.isEmpty())
+ return false;
+ if(g_VersionFilterData.forgeInstallerBlacklist.contains(mcver))
+ return false;
+ return true;
+}
+
+QString ForgeVersion::filename()
+{
+ return usesInstaller() ? installer_filename : universal_filename;
+}
+
+QString ForgeVersion::url()
+{
+ return usesInstaller() ? installer_url : universal_url;
+}
diff --git a/logic/forge/ForgeVersion.h b/logic/forge/ForgeVersion.h
new file mode 100644
index 00000000..466f46bd
--- /dev/null
+++ b/logic/forge/ForgeVersion.h
@@ -0,0 +1,33 @@
+#pragma once
+#include <QString>
+#include <memory>
+#include "logic/BaseVersion.h"
+
+struct ForgeVersion;
+typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
+
+struct ForgeVersion : public BaseVersion
+{
+ virtual QString descriptor() override;
+ virtual QString name() override;
+ virtual QString typeString() const override;
+ virtual bool operator<(BaseVersion &a) override;
+ virtual bool operator>(BaseVersion &a) override;
+
+ QString filename();
+ QString url();
+
+ bool usesInstaller();
+
+ int m_buildnr = 0;
+ QString branch;
+ QString universal_url;
+ QString changelog_url;
+ QString installer_url;
+ QString jobbuildver;
+ QString mcver;
+ QString mcver_sane;
+ QString universal_filename;
+ QString installer_filename;
+ bool is_recommended = false;
+};
diff --git a/logic/lists/ForgeVersionList.cpp b/logic/forge/ForgeVersionList.cpp
index 4902dc64..efb30ba6 100644
--- a/logic/lists/ForgeVersionList.cpp
+++ b/logic/forge/ForgeVersionList.cpp
@@ -13,9 +13,10 @@
* limitations under the License.
*/
-#include "ForgeVersionList.h"
-#include <logic/net/NetJob.h>
-#include <logic/net/URLConstants.h>
+#include "logic/forge/ForgeVersionList.h"
+#include "logic/forge/ForgeVersion.h"
+#include "logic/net/NetJob.h"
+#include "logic/net/URLConstants.h"
#include "MultiMC.h"
#include <QtNetwork>
@@ -71,7 +72,7 @@ QVariant ForgeVersionList::data(const QModelIndex &index, int role) const
return version->name();
case 1:
- return version->mcver;
+ return version->mcver_sane;
case 2:
return version->typeString();
@@ -236,7 +237,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
if (!build_nr)
continue;
QJsonArray files = obj.value("files").toArray();
- QString url, jobbuildver, mcver, buildtype, filename;
+ QString url, jobbuildver, mcver, buildtype, universal_filename;
QString changelog_url, installer_url;
QString installer_filename;
bool valid = false;
@@ -254,7 +255,7 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
url = file.value("url").toString();
jobbuildver = file.value("jobbuildver").toString();
int lastSlash = url.lastIndexOf('/');
- filename = url.mid(lastSlash + 1);
+ universal_filename = url.mid(lastSlash + 1);
valid = true;
}
else if (buildtype == "changelog")
@@ -281,15 +282,9 @@ bool ForgeListLoadTask::parseForgeList(QList<BaseVersionPtr> &out)
fVersion->changelog_url = changelog_url;
fVersion->installer_url = installer_url;
fVersion->jobbuildver = jobbuildver;
- fVersion->mcver = mcver;
- if (installer_filename.isEmpty())
- {
- fVersion->filename = filename;
- }
- else
- {
- fVersion->filename = installer_filename;
- }
+ fVersion->mcver = fVersion->mcver_sane = mcver;
+ fVersion->installer_filename = installer_filename;
+ fVersion->universal_filename = universal_filename;
fVersion->m_buildnr = build_nr;
out.append(fVersion);
}
@@ -341,9 +336,17 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
std::shared_ptr<ForgeVersion> fVersion(new ForgeVersion());
fVersion->m_buildnr = number.value("build").toDouble();
fVersion->jobbuildver = number.value("version").toString();
+ fVersion->branch = number.value("branch").toString("");
fVersion->mcver = number.value("mcversion").toString();
- fVersion->filename = "";
- QString filename, installer_filename;
+ fVersion->universal_filename = "";
+ fVersion->installer_filename = "";
+ // HACK: here, we fix the minecraft version used by forge.
+ // HACK: this will inevitably break (later)
+ // FIXME: replace with a dictionary
+ fVersion->mcver_sane = fVersion->mcver;
+ fVersion->mcver_sane.replace("_pre", "-pre");
+
+ QString universal_filename, installer_filename;
QJsonArray files = number.value("files").toArray();
for (auto fIt = files.begin(); fIt != files.end(); ++fIt)
{
@@ -353,37 +356,47 @@ bool ForgeListLoadTask::parseForgeGradleList(QList<BaseVersionPtr> &out)
{
continue;
}
- if (file.at(1).toString() == "installer")
+
+ QString extension = file.at(0).toString();
+ QString part = file.at(1).toString();
+ QString checksum = file.at(2).toString();
+
+ // insane form of mcver is used here
+ QString longVersion = fVersion->mcver + "-" + fVersion->jobbuildver;
+ if (!fVersion->branch.isEmpty())
+ {
+ longVersion = longVersion + "-" + fVersion->branch;
+ }
+ QString filename = artifact + "-" + longVersion + "-" + part + "." + extension;
+
+ QString url = QString("%1/%2/%3")
+ .arg(webpath)
+ .arg(longVersion)
+ .arg(filename);
+
+ if (part == "installer")
{
- fVersion->installer_url = QString("%1/%2-%3/%4-%2-%3-installer.%5").arg(
- webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
- file.at(0).toString());
- installer_filename = QString("%1-%2-%3-installer.%4").arg(
- artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
+ fVersion->installer_url = url;
+ installer_filename = filename;
}
- else if (file.at(1).toString() == "universal")
+ else if (part == "universal")
{
- fVersion->universal_url = QString("%1/%2-%3/%4-%2-%3-universal.%5").arg(
- webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
- file.at(0).toString());
- filename = QString("%1-%2-%3-universal.%4").arg(
- artifact, fVersion->mcver, fVersion->jobbuildver, file.at(0).toString());
+ fVersion->universal_url = url;
+ universal_filename = filename;
}
- else if (file.at(1).toString() == "changelog")
+ else if (part == "changelog")
{
- fVersion->changelog_url = QString("%1/%2-%3/%4-%2-%3-changelog.%5").arg(
- webpath, fVersion->mcver, fVersion->jobbuildver, artifact,
- file.at(0).toString());
+ fVersion->changelog_url = url;
}
}
if (fVersion->installer_url.isEmpty() && fVersion->universal_url.isEmpty())
{
continue;
}
- fVersion->filename = fVersion->installer_url.isEmpty() ? filename : installer_filename;
+ fVersion->universal_filename = universal_filename;
+ fVersion->installer_filename = installer_filename;
out.append(fVersion);
}
-
return true;
}
@@ -425,6 +438,7 @@ void ForgeListLoadTask::listFailed()
QLOG_ERROR() << "Getting forge version list failed for reasons unknown.";
}
}
+
void ForgeListLoadTask::gradleListFailed()
{
auto reply = gradleListDownload->m_reply;
diff --git a/logic/lists/ForgeVersionList.h b/logic/forge/ForgeVersionList.h
index b19d3f56..477edb3d 100644
--- a/logic/lists/ForgeVersionList.h
+++ b/logic/forge/ForgeVersionList.h
@@ -18,57 +18,12 @@
#include <QObject>
#include <QAbstractListModel>
#include <QUrl>
-
#include <QNetworkReply>
-#include "BaseVersionList.h"
+
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/net/NetJob.h"
-
-class ForgeVersion;
-typedef std::shared_ptr<ForgeVersion> ForgeVersionPtr;
-
-struct ForgeVersion : public BaseVersion
-{
- virtual QString descriptor() override
- {
- return filename;
- }
- ;
- virtual QString name() override
- {
- return "Forge " + jobbuildver;
- }
- ;
- virtual QString typeString() const override
- {
- if (installer_url.isEmpty())
- return "Universal";
- else
- return "Installer";
- }
-
- virtual bool operator<(BaseVersion &a) override
- {
- ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
- if(!pa)
- return true;
- return m_buildnr < pa->m_buildnr;
- }
- virtual bool operator>(BaseVersion &a) override
- {
- ForgeVersion *pa = dynamic_cast<ForgeVersion *>(&a);
- if(!pa)
- return false;
- return m_buildnr > pa->m_buildnr;
- }
- int m_buildnr = 0;
- QString universal_url;
- QString changelog_url;
- QString installer_url;
- QString jobbuildver;
- QString mcver;
- QString filename;
-};
+#include "logic/forge/ForgeVersion.h"
class ForgeVersionList : public BaseVersionList
{
@@ -86,6 +41,8 @@ public:
virtual BaseVersionPtr getLatestStable() const;
+ ForgeVersionPtr findVersionByVersionNr(QString version);
+
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int columnCount(const QModelIndex &parent) const;
diff --git a/logic/net/ForgeXzDownload.cpp b/logic/forge/ForgeXzDownload.cpp
index 359ad858..359ad858 100644
--- a/logic/net/ForgeXzDownload.cpp
+++ b/logic/forge/ForgeXzDownload.cpp
diff --git a/logic/net/ForgeXzDownload.h b/logic/forge/ForgeXzDownload.h
index 7bdfb6d9..f2564380 100644
--- a/logic/net/ForgeXzDownload.h
+++ b/logic/forge/ForgeXzDownload.h
@@ -15,8 +15,8 @@
#pragma once
-#include "NetAction.h"
-#include "HttpMetaCache.h"
+#include "logic/net/NetAction.h"
+#include "logic/net/HttpMetaCache.h"
#include <QFile>
#include <QTemporaryFile>
#include "ForgeMirror.h"
diff --git a/logic/LegacyForge.cpp b/logic/forge/LegacyForge.cpp
index 94212ae4..94212ae4 100644
--- a/logic/LegacyForge.cpp
+++ b/logic/forge/LegacyForge.cpp
diff --git a/logic/LegacyForge.h b/logic/forge/LegacyForge.h
index f4165ffa..ec49f63c 100644
--- a/logic/LegacyForge.h
+++ b/logic/forge/LegacyForge.h
@@ -15,7 +15,7 @@
#pragma once
-#include "Mod.h"
+#include "logic/Mod.h"
class MinecraftForge : public Mod
{
diff --git a/logic/JavaChecker.cpp b/logic/java/JavaChecker.cpp
index b87ee3d5..b87ee3d5 100644
--- a/logic/JavaChecker.cpp
+++ b/logic/java/JavaChecker.cpp
diff --git a/logic/JavaChecker.h b/logic/java/JavaChecker.h
index e19895f7..e19895f7 100644
--- a/logic/JavaChecker.h
+++ b/logic/java/JavaChecker.h
diff --git a/logic/JavaCheckerJob.cpp b/logic/java/JavaCheckerJob.cpp
index b0aea758..b0aea758 100644
--- a/logic/JavaCheckerJob.cpp
+++ b/logic/java/JavaCheckerJob.cpp
diff --git a/logic/JavaCheckerJob.h b/logic/java/JavaCheckerJob.h
index 132a92d4..132a92d4 100644
--- a/logic/JavaCheckerJob.h
+++ b/logic/java/JavaCheckerJob.h
diff --git a/logic/JavaUtils.cpp b/logic/java/JavaUtils.cpp
index 3a3046bd..09719c73 100644
--- a/logic/JavaUtils.cpp
+++ b/logic/java/JavaUtils.cpp
@@ -16,7 +16,6 @@
#include <QStringList>
#include <QString>
#include <QDir>
-#include <QMessageBox>
#include <QStringList>
#include <setting.h>
@@ -24,11 +23,10 @@
#include "MultiMC.h"
-#include "JavaUtils.h"
#include "logger/QsLog.h"
-#include "gui/dialogs/VersionSelectDialog.h"
-#include "JavaCheckerJob.h"
-#include "lists/JavaVersionList.h"
+#include "logic/java/JavaUtils.h"
+#include "logic/java/JavaCheckerJob.h"
+#include "logic/java/JavaVersionList.h"
JavaUtils::JavaUtils()
{
@@ -124,7 +122,7 @@ QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString
javaVersion->id = subKeyName;
javaVersion->arch = archType;
javaVersion->path =
- QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe");
+ QDir(PathCombine(value, "bin")).absoluteFilePath("javaw.exe");
javas.append(javaVersion);
}
@@ -154,12 +152,12 @@ QList<QString> JavaUtils::FindJavaPaths()
KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit");
java_candidates.append(JRE64s);
- java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/java.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/java.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK64s);
java_candidates.append(JRE32s);
- java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/java.exe"));
- java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/java.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/javaw.exe"));
+ java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/javaw.exe"));
java_candidates.append(JDK32s);
java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path));
diff --git a/logic/JavaUtils.h b/logic/java/JavaUtils.h
index 22a68ef3..af92100f 100644
--- a/logic/JavaUtils.h
+++ b/logic/java/JavaUtils.h
@@ -21,7 +21,7 @@
#include <osutils.h>
#include "JavaCheckerJob.h"
#include "JavaChecker.h"
-#include "lists/JavaVersionList.h"
+#include "JavaVersionList.h"
#if WINDOWS
#include <windows.h>
diff --git a/logic/lists/JavaVersionList.cpp b/logic/java/JavaVersionList.cpp
index 4fd0bc19..dcb6ced6 100644
--- a/logic/lists/JavaVersionList.cpp
+++ b/logic/java/JavaVersionList.cpp
@@ -13,16 +13,16 @@
* limitations under the License.
*/
-#include "JavaVersionList.h"
-#include "MultiMC.h"
-
#include <QtNetwork>
#include <QtXml>
#include <QRegExp>
+#include "MultiMC.h"
#include "logger/QsLog.h"
-#include "logic/JavaCheckerJob.h"
-#include "logic/JavaUtils.h"
+
+#include "logic/java/JavaVersionList.h"
+#include "logic/java/JavaCheckerJob.h"
+#include "logic/java/JavaUtils.h"
JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent)
{
diff --git a/logic/lists/JavaVersionList.h b/logic/java/JavaVersionList.h
index e6cc8e5f..a46f33a2 100644
--- a/logic/lists/JavaVersionList.h
+++ b/logic/java/JavaVersionList.h
@@ -18,9 +18,9 @@
#include <QObject>
#include <QAbstractListModel>
-#include "BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
-#include "logic/JavaCheckerJob.h"
+#include "logic/java/JavaCheckerJob.h"
class JavaListLoadTask;
diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp
deleted file mode 100644
index b9d60c61..00000000
--- a/logic/lists/MinecraftVersionList.cpp
+++ /dev/null
@@ -1,290 +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 "MinecraftVersionList.h"
-#include "MultiMC.h"
-#include "logic/net/URLConstants.h"
-
-#include <QtXml>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-#include <QJsonValue>
-#include <QJsonParseError>
-
-#include <QtAlgorithms>
-
-#include <QtNetwork>
-
-MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
-{
-}
-
-Task *MinecraftVersionList::getLoadTask()
-{
- return new MCVListLoadTask(this);
-}
-
-bool MinecraftVersionList::isLoaded()
-{
- return m_loaded;
-}
-
-const BaseVersionPtr MinecraftVersionList::at(int i) const
-{
- return m_vlist.at(i);
-}
-
-int MinecraftVersionList::count() const
-{
- return m_vlist.count();
-}
-
-static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
-{
- auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
- auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
- return left->timestamp > right->timestamp;
-}
-
-void MinecraftVersionList::sortInternal()
-{
- qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
-}
-
-void MinecraftVersionList::sort()
-{
- beginResetModel();
- sortInternal();
- endResetModel();
-}
-
-BaseVersionPtr MinecraftVersionList::getLatestStable() const
-{
- for (int i = 0; i < m_vlist.length(); i++)
- {
- auto ver = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist.at(i));
- if (ver->is_latest && !ver->is_snapshot)
- {
- return m_vlist.at(i);
- }
- }
- return BaseVersionPtr();
-}
-
-void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
-{
- beginResetModel();
- m_vlist = versions;
- m_loaded = true;
- sortInternal();
- endResetModel();
-}
-
-inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
-{
- QDomNodeList elementList = parent.elementsByTagName(tagname);
- if (elementList.count())
- return elementList.at(0).toElement();
- else
- return QDomElement();
-}
-
-inline QDateTime timeFromS3Time(QString str)
-{
- return QDateTime::fromString(str, Qt::ISODate);
-}
-
-MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
-{
- m_list = vlist;
- m_currentStable = NULL;
- vlistReply = nullptr;
- legacyWhitelist.insert("1.5.2");
- legacyWhitelist.insert("1.5.1");
- legacyWhitelist.insert("1.5");
- legacyWhitelist.insert("1.4.7");
- legacyWhitelist.insert("1.4.6");
- legacyWhitelist.insert("1.4.5");
- legacyWhitelist.insert("1.4.4");
- legacyWhitelist.insert("1.4.3");
- legacyWhitelist.insert("1.4.2");
- legacyWhitelist.insert("1.4.1");
- legacyWhitelist.insert("1.4");
- legacyWhitelist.insert("1.3.2");
- legacyWhitelist.insert("1.3.1");
- legacyWhitelist.insert("1.3");
- legacyWhitelist.insert("1.2.5");
- legacyWhitelist.insert("1.2.4");
- legacyWhitelist.insert("1.2.3");
- legacyWhitelist.insert("1.2.2");
- legacyWhitelist.insert("1.2.1");
- legacyWhitelist.insert("1.1");
- legacyWhitelist.insert("1.0.1");
- legacyWhitelist.insert("1.0");
-}
-
-MCVListLoadTask::~MCVListLoadTask()
-{
-}
-
-void MCVListLoadTask::executeTask()
-{
- setStatus(tr("Loading instance version list..."));
- auto worker = MMC->qnam();
- vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
- connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
-}
-
-void MCVListLoadTask::list_downloaded()
-{
- if (vlistReply->error() != QNetworkReply::NoError)
- {
- vlistReply->deleteLater();
- emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
- return;
- }
-
- QJsonParseError jsonError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError);
- vlistReply->deleteLater();
-
- if (jsonError.error != QJsonParseError::NoError)
- {
- emitFailed("Error parsing version list JSON:" + jsonError.errorString());
- return;
- }
-
- if (!jsonDoc.isObject())
- {
- emitFailed("Error parsing version list JSON: jsonDoc is not an object");
- return;
- }
-
- QJsonObject root = jsonDoc.object();
-
- // Get the ID of the latest release and the latest snapshot.
- if (!root.value("latest").isObject())
- {
- emitFailed("Error parsing version list JSON: version list is missing 'latest' object");
- return;
- }
-
- QJsonObject latest = root.value("latest").toObject();
-
- QString latestReleaseID = latest.value("release").toString("");
- QString latestSnapshotID = latest.value("snapshot").toString("");
- if (latestReleaseID.isEmpty())
- {
- emitFailed("Error parsing version list JSON: latest release field is missing");
- return;
- }
- if (latestSnapshotID.isEmpty())
- {
- emitFailed("Error parsing version list JSON: latest snapshot field is missing");
- return;
- }
-
- // Now, get the array of versions.
- if (!root.value("versions").isArray())
- {
- emitFailed(
- "Error parsing version list JSON: version list object is missing 'versions' array");
- return;
- }
- QJsonArray versions = root.value("versions").toArray();
-
- QList<BaseVersionPtr> tempList;
- for (int i = 0; i < versions.count(); i++)
- {
- bool is_snapshot = false;
- bool is_latest = false;
-
- // Load the version info.
- if (!versions[i].isObject())
- {
- // FIXME: log this somewhere
- continue;
- }
- QJsonObject version = versions[i].toObject();
- QString versionID = version.value("id").toString("");
- QString versionTimeStr = version.value("releaseTime").toString("");
- QString versionTypeStr = version.value("type").toString("");
- if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty())
- {
- // FIXME: log this somewhere
- continue;
- }
-
- // Parse the timestamp.
- QDateTime versionTime = timeFromS3Time(versionTimeStr);
- if (!versionTime.isValid())
- {
- // FIXME: log this somewhere
- continue;
- }
- // Parse the type.
- MinecraftVersion::VersionType versionType;
- // OneSix or Legacy. use filter to determine type
- if (versionTypeStr == "release")
- {
- versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
- : MinecraftVersion::OneSix;
- is_latest = (versionID == latestReleaseID);
- is_snapshot = false;
- }
- else if (versionTypeStr == "snapshot") // It's a snapshot... yay
- {
- versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy
- : MinecraftVersion::OneSix;
- is_latest = (versionID == latestSnapshotID);
- is_snapshot = true;
- }
- else if (versionTypeStr == "old_alpha")
- {
- versionType = MinecraftVersion::Nostalgia;
- is_latest = false;
- is_snapshot = false;
- }
- else if (versionTypeStr == "old_beta")
- {
- versionType = MinecraftVersion::Legacy;
- is_latest = false;
- is_snapshot = false;
- }
- else
- {
- // FIXME: log this somewhere
- continue;
- }
- // Get the download URL.
- QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
-
- // Now, we construct the version object and add it to the list.
- std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
- mcVersion->m_name = mcVersion->m_descriptor = versionID;
- mcVersion->timestamp = versionTime.toMSecsSinceEpoch();
- mcVersion->download_url = dlUrl;
- mcVersion->is_latest = is_latest;
- mcVersion->is_snapshot = is_snapshot;
- mcVersion->type = versionType;
- tempList.append(mcVersion);
- }
- m_list->updateListData(tempList);
-
- emitSucceeded();
- return;
-}
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/liteloader/LiteLoaderInstaller.cpp
index 99cc5643..ea1a4396 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/liteloader/LiteLoaderInstaller.cpp
@@ -20,11 +20,11 @@
#include "logger/QsLog.h"
-#include "VersionFinal.h"
-#include "OneSixLibrary.h"
-#include "OneSixInstance.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/OneSixInstance.h"
#include "MultiMC.h"
-#include "lists/LiteLoaderVersionList.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller()
{
diff --git a/logic/LiteLoaderInstaller.h b/logic/liteloader/LiteLoaderInstaller.h
index 3ab5acb2..43ad6b83 100644
--- a/logic/LiteLoaderInstaller.h
+++ b/logic/liteloader/LiteLoaderInstaller.h
@@ -15,12 +15,11 @@
#pragma once
-#include "BaseInstaller.h"
-
#include <QString>
#include <QMap>
-#include "logic/lists/LiteLoaderVersionList.h"
+#include "logic/BaseInstaller.h"
+#include "logic/liteloader/LiteLoaderVersionList.h"
class LiteLoaderInstaller : public BaseInstaller
{
diff --git a/logic/lists/LiteLoaderVersionList.cpp b/logic/liteloader/LiteLoaderVersionList.cpp
index ef95eefd..ef95eefd 100644
--- a/logic/lists/LiteLoaderVersionList.cpp
+++ b/logic/liteloader/LiteLoaderVersionList.cpp
diff --git a/logic/lists/LiteLoaderVersionList.h b/logic/liteloader/LiteLoaderVersionList.h
index bfc913e5..0aecc3e1 100644
--- a/logic/lists/LiteLoaderVersionList.h
+++ b/logic/liteloader/LiteLoaderVersionList.h
@@ -19,10 +19,10 @@
#include <QString>
#include <QStringList>
-#include "BaseVersionList.h"
-#include "logic/tasks/Task.h"
#include "logic/BaseVersion.h"
-#include <logic/net/NetJob.h>
+#include "logic/BaseVersionList.h"
+#include "logic/tasks/Task.h"
+#include "logic/net/NetJob.h"
class LLListLoadTask;
class QNetworkReply;
diff --git a/logic/minecraft/InstanceVersion.cpp b/logic/minecraft/InstanceVersion.cpp
new file mode 100644
index 00000000..ca0e3796
--- /dev/null
+++ b/logic/minecraft/InstanceVersion.cpp
@@ -0,0 +1,537 @@
+/* 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 <QDebug>
+#include <QFile>
+#include <QDir>
+#include <QUuid>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <pathutils.h>
+
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/VersionBuilder.h"
+#include "logic/OneSixInstance.h"
+
+InstanceVersion::InstanceVersion(OneSixInstance *instance, QObject *parent)
+ : QAbstractListModel(parent), m_instance(instance)
+{
+ clear();
+}
+
+void InstanceVersion::reload(const QStringList &external)
+{
+ m_externalPatches = external;
+ beginResetModel();
+ VersionBuilder::build(this, m_instance, m_externalPatches);
+ reapply(true);
+ endResetModel();
+}
+
+void InstanceVersion::clear()
+{
+ id.clear();
+ m_updateTimeString.clear();
+ m_updateTime = QDateTime();
+ m_releaseTimeString.clear();
+ m_releaseTime = QDateTime();
+ type.clear();
+ assets.clear();
+ processArguments.clear();
+ minecraftArguments.clear();
+ minimumLauncherVersion = 0xDEADBEAF;
+ mainClass.clear();
+ appletClass.clear();
+ libraries.clear();
+ tweakers.clear();
+ jarMods.clear();
+ traits.clear();
+}
+
+bool InstanceVersion::canRemove(const int index) const
+{
+ return VersionPatches.at(index)->isMoveable();
+}
+
+bool InstanceVersion::preremove(VersionPatchPtr patch)
+{
+ bool ok = true;
+ for(auto & jarmod: patch->getJarMods())
+ {
+ QString fullpath =PathCombine(m_instance->jarModsDir(), jarmod->name);
+ QFileInfo finfo (fullpath);
+ if(finfo.exists())
+ ok &= QFile::remove(fullpath);
+ }
+ return ok;
+}
+
+bool InstanceVersion::remove(const int index)
+{
+ if (!canRemove(index))
+ return false;
+ if(!preremove(VersionPatches[index]))
+ {
+ return false;
+ }
+ if(!QFile::remove(VersionPatches.at(index)->getPatchFilename()))
+ return false;
+ beginRemoveRows(QModelIndex(), index, index);
+ VersionPatches.removeAt(index);
+ endRemoveRows();
+ reapply(true);
+ saveCurrentOrder();
+ return true;
+}
+
+bool InstanceVersion::remove(const QString id)
+{
+ int i = 0;
+ for (auto patch : VersionPatches)
+ {
+ if (patch->getPatchID() == id)
+ {
+ return remove(i);
+ }
+ i++;
+ }
+ return false;
+}
+
+QString InstanceVersion::versionFileId(const int index) const
+{
+ if (index < 0 || index >= VersionPatches.size())
+ {
+ return QString();
+ }
+ return VersionPatches.at(index)->getPatchID();
+}
+
+VersionPatchPtr InstanceVersion::versionPatch(const QString &id)
+{
+ for (auto file : VersionPatches)
+ {
+ if (file->getPatchID() == id)
+ {
+ return file;
+ }
+ }
+ return 0;
+}
+
+VersionPatchPtr InstanceVersion::versionPatch(int index)
+{
+ if(index < 0 || index >= VersionPatches.size())
+ return 0;
+ return VersionPatches[index];
+}
+
+
+bool InstanceVersion::hasJarMods()
+{
+ return !jarMods.isEmpty();
+}
+
+bool InstanceVersion::hasFtbPack()
+{
+ return versionPatch("org.multimc.ftb.pack.json") != nullptr;
+}
+
+bool InstanceVersion::removeFtbPack()
+{
+ return remove("org.multimc.ftb.pack.json");
+}
+
+bool InstanceVersion::isVanilla()
+{
+ QDir patches(PathCombine(m_instance->instanceRoot(), "patches/"));
+ if(VersionPatches.size() > 1)
+ return false;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
+ return false;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
+ return false;
+ return true;
+}
+
+bool InstanceVersion::revertToVanilla()
+{
+ beginResetModel();
+ // remove custom.json, if present
+ QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
+ if(QFile::exists(customPath))
+ {
+ if(!QFile::remove(customPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove version.json, if present
+ QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
+ if(QFile::exists(versionPath))
+ {
+ if(!QFile::remove(versionPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove patches, if present
+ auto it = VersionPatches.begin();
+ while (it != VersionPatches.end())
+ {
+ if ((*it)->isMoveable())
+ {
+ if(!preremove(*it))
+ {
+ endResetModel();
+ saveCurrentOrder();
+ return false;
+ }
+ if(!QFile::remove((*it)->getPatchFilename()))
+ {
+ endResetModel();
+ saveCurrentOrder();
+ return false;
+ }
+ it = VersionPatches.erase(it);
+ }
+ else
+ it++;
+ }
+ reapply(true);
+ endResetModel();
+ saveCurrentOrder();
+ return true;
+}
+
+bool InstanceVersion::hasDeprecatedVersionFiles()
+{
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "custom.json")))
+ return true;
+ if(QFile::exists(PathCombine(m_instance->instanceRoot(), "version.json")))
+ return true;
+ return false;
+}
+
+bool InstanceVersion::removeDeprecatedVersionFiles()
+{
+ beginResetModel();
+ // remove custom.json, if present
+ QString customPath = PathCombine(m_instance->instanceRoot(), "custom.json");
+ if(QFile::exists(customPath))
+ {
+ if(!QFile::remove(customPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ // remove version.json, if present
+ QString versionPath = PathCombine(m_instance->instanceRoot(), "version.json");
+ if(QFile::exists(versionPath))
+ {
+ if(!QFile::remove(versionPath))
+ {
+ endResetModel();
+ return false;
+ }
+ }
+ endResetModel();
+ return true;
+}
+
+QList<std::shared_ptr<OneSixLibrary> > InstanceVersion::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> > InstanceVersion::getActiveNativeLibs()
+{
+ QList<std::shared_ptr<OneSixLibrary> > output;
+ for (auto lib : libraries)
+ {
+ if (lib->isActive() && lib->isNative())
+ {
+ output.append(lib);
+ }
+ }
+ return output;
+}
+
+std::shared_ptr<InstanceVersion> InstanceVersion::fromJson(const QJsonObject &obj)
+{
+ std::shared_ptr<InstanceVersion> version(new InstanceVersion(0));
+ try
+ {
+ VersionBuilder::readJsonAndApplyToVersion(version.get(), obj);
+ }
+ catch(MMCError & err)
+ {
+ return 0;
+ }
+ return version;
+}
+
+QVariant InstanceVersion::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ int row = index.row();
+ int column = index.column();
+
+ if (row < 0 || row >= VersionPatches.size())
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ {
+ switch (column)
+ {
+ case 0:
+ return VersionPatches.at(row)->getPatchName();
+ case 1:
+ return VersionPatches.at(row)->getPatchVersion();
+ default:
+ return QVariant();
+ }
+ }
+ return QVariant();
+}
+QVariant InstanceVersion::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 InstanceVersion::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+}
+
+int InstanceVersion::rowCount(const QModelIndex &parent) const
+{
+ return VersionPatches.size();
+}
+
+int InstanceVersion::columnCount(const QModelIndex &parent) const
+{
+ return 2;
+}
+
+void InstanceVersion::saveCurrentOrder() const
+{
+ PatchOrder order;
+ for(auto item: VersionPatches)
+ {
+ if(!item->isMoveable())
+ continue;
+ order.append(item->getPatchID());
+ }
+ VersionBuilder::writeOverrideOrders(m_instance, order);
+}
+
+void InstanceVersion::move(const int index, const MoveDirection direction)
+{
+ int theirIndex;
+ if (direction == MoveUp)
+ {
+ theirIndex = index - 1;
+ }
+ else
+ {
+ theirIndex = index + 1;
+ }
+
+ if (index < 0 || index >= VersionPatches.size())
+ return;
+ if (theirIndex >= rowCount())
+ theirIndex = rowCount() - 1;
+ if (theirIndex == -1)
+ theirIndex = rowCount() - 1;
+ if (index == theirIndex)
+ return;
+ int togap = theirIndex > index ? theirIndex + 1 : theirIndex;
+
+ auto from = versionPatch(index);
+ auto to = versionPatch(theirIndex);
+
+ if (!from || !to || !to->isMoveable() || !from->isMoveable())
+ {
+ return;
+ }
+ beginMoveRows(QModelIndex(), index, index, QModelIndex(), togap);
+ VersionPatches.swap(index, theirIndex);
+ endMoveRows();
+ saveCurrentOrder();
+ reapply();
+}
+void InstanceVersion::resetOrder()
+{
+ QDir(m_instance->instanceRoot()).remove("order.json");
+ reload(m_externalPatches);
+}
+
+void InstanceVersion::reapply(const bool alreadyReseting)
+{
+ clear();
+ for(auto file: VersionPatches)
+ {
+ file->applyTo(this);
+ }
+ finalize();
+}
+
+void InstanceVersion::finalize()
+{
+ // HACK: deny april fools. my head hurts enough already.
+ QDate now = QDate::currentDate();
+ bool isAprilFools = now.month() == 4 && now.day() == 1;
+ if (assets.endsWith("_af") && !isAprilFools)
+ {
+ assets = assets.left(assets.length() - 3);
+ }
+ if (assets.isEmpty())
+ {
+ assets = "legacy";
+ }
+ auto finalizeArguments = [&]( QString & minecraftArguments, const QString & processArguments ) -> void
+ {
+ if (!minecraftArguments.isEmpty())
+ return;
+ QString toCompare = processArguments.toLower();
+ if (toCompare == "legacy")
+ {
+ minecraftArguments = " ${auth_player_name} ${auth_session}";
+ }
+ else if (toCompare == "username_session")
+ {
+ minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
+ }
+ else if (toCompare == "username_session_version")
+ {
+ minecraftArguments = "--username ${auth_player_name} "
+ "--session ${auth_session} "
+ "--version ${profile_name}";
+ }
+ };
+ finalizeArguments(vanillaMinecraftArguments, vanillaProcessArguments);
+ finalizeArguments(minecraftArguments, processArguments);
+}
+
+void InstanceVersion::installJarMods(QStringList selectedFiles)
+{
+ for(auto filename: selectedFiles)
+ {
+ installJarModByFilename(filename);
+ }
+}
+
+void InstanceVersion::installJarModByFilename(QString filepath)
+{
+ QString patchDir = PathCombine(m_instance->instanceRoot(), "patches");
+ if(!ensureFolderPathExists(patchDir))
+ {
+ // THROW...
+ return;
+ }
+
+ if (!ensureFolderPathExists(m_instance->jarModsDir()))
+ {
+ // THROW...
+ return;
+ }
+
+ QFileInfo sourceInfo(filepath);
+ auto uuid = QUuid::createUuid();
+ QString id = uuid.toString().remove('{').remove('}');
+ QString target_filename = id + ".jar";
+ QString target_id = "org.multimc.jarmod." + id;
+ QString target_name = sourceInfo.completeBaseName() + " (jar mod)";
+ QString finalPath = PathCombine(m_instance->jarModsDir(), target_filename);
+
+ QFileInfo targetInfo(finalPath);
+ if(targetInfo.exists())
+ {
+ // THROW
+ return;
+ }
+
+ if (!QFile::copy(sourceInfo.absoluteFilePath(),QFileInfo(finalPath).absoluteFilePath()))
+ {
+ // THROW
+ return;
+ }
+
+ auto f = std::make_shared<VersionFile>();
+ auto jarMod = std::make_shared<Jarmod>();
+ jarMod->name = target_filename;
+ f->jarMods.append(jarMod);
+ f->name = target_name;
+ f->fileId = target_id;
+ f->order = getFreeOrderNumber();
+
+ QFile file(PathCombine(patchDir, target_id + ".json"));
+ if (!file.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Error opening" << file.fileName()
+ << "for reading:" << file.errorString();
+ return;
+ // THROW
+ }
+ file.write(f->toJson(true).toJson());
+ file.close();
+ int index = VersionPatches.size();
+ beginInsertRows(QModelIndex(), index, index);
+ VersionPatches.append(f);
+ endInsertRows();
+ saveCurrentOrder();
+}
+
+int InstanceVersion::getFreeOrderNumber()
+{
+ int largest = 100;
+ // yes, I do realize this is dumb. The order thing itself is dumb. and to be removed next.
+ for(auto thing: VersionPatches)
+ {
+ int order = thing->getOrder();
+ if(order > largest)
+ largest = order;
+ }
+ return largest + 1;
+}
diff --git a/logic/VersionFinal.h b/logic/minecraft/InstanceVersion.h
index 41fd23bd..6b69ab47 100644
--- a/logic/VersionFinal.h
+++ b/logic/minecraft/InstanceVersion.h
@@ -23,14 +23,15 @@
#include "OneSixLibrary.h"
#include "VersionFile.h"
+#include "JarMod.h"
class OneSixInstance;
-class VersionFinal : public QAbstractListModel
+class InstanceVersion : public QAbstractListModel
{
Q_OBJECT
public:
- explicit VersionFinal(OneSixInstance *instance, QObject *parent = 0);
+ explicit InstanceVersion(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;
@@ -38,22 +39,32 @@ public:
virtual int columnCount(const QModelIndex &parent) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
- void reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
+ void reload(const QStringList &external = QStringList());
void clear();
bool canRemove(const int index) const;
QString versionFileId(const int index) const;
- // does this instance have an all overriding custom.json
- bool isCustom();
- // remove custom.json
- bool revertToBase();
- // does this instance have an FTB pack patch file?
+ // is this version unmodded vanilla minecraft?
+ bool isVanilla();
+ // remove any customizations on top of vanilla
+ bool revertToVanilla();
+
+ // does this version consist of obsolete files?
+ bool hasDeprecatedVersionFiles();
+ // remove obsolete files
+ bool removeDeprecatedVersionFiles();
+
+ // does this version have an FTB pack patch file?
bool hasFtbPack();
// remove FTB pack
bool removeFtbPack();
+ // does this version have any jar mods?
+ bool hasJarMods();
+ void installJarMods(QStringList selectedFiles);
+ void installJarModByFilename(QString filepath);
enum MoveDirection { MoveUp, MoveDown };
void move(const int index, const MoveDirection direction);
@@ -72,16 +83,24 @@ public:
QList<std::shared_ptr<OneSixLibrary>> getActiveNormalLibs();
QList<std::shared_ptr<OneSixLibrary>> getActiveNativeLibs();
- static std::shared_ptr<VersionFinal> fromJson(const QJsonObject &obj);
+ static std::shared_ptr<InstanceVersion> fromJson(const QJsonObject &obj);
+private:
+ bool preremove(VersionPatchPtr patch);
+
// data members
public:
/// the ID - determines which jar to use! ACTUALLY IMPORTANT!
QString id;
- /// Last updated time - as a string
- QString time;
- /// Release time - as a string
- QString releaseTime;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
/// Release type - "release" or "snapshot"
QString type;
/// Assets type - "legacy" or a version ID
@@ -91,6 +110,8 @@ public:
* ex: "username_session_version"
*/
QString processArguments;
+ /// Same as above, but only for vanilla
+ QString vanillaProcessArguments;
/**
* arguments that should be used for launching minecraft
*
@@ -98,6 +119,8 @@ public:
* --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}"
*/
QString minecraftArguments;
+ /// Same as above, but only for vanilla
+ QString vanillaMinecraftArguments;
/**
* the minimum launcher version required by this version ... current is 4 (at point of
* writing)
@@ -111,9 +134,22 @@ public:
* The main class to load first
*/
QString mainClass;
-
+ /**
+ * The applet class, for some very old minecraft releases
+ */
+ QString appletClass;
+
/// the list of libs - both active and inactive, native and java
QList<std::shared_ptr<OneSixLibrary>> libraries;
+
+ /// same, but only vanilla.
+ QList<std::shared_ptr<OneSixLibrary>> vanillaLibraries;
+
+ /// traits, collected from all the version files (version files can only add)
+ QSet<QString> traits;
+
+ /// A list of jar mods. version files can add those.
+ QList<JarmodPtr> jarMods;
/*
FIXME: add support for those rules here? Looks like a pile of quick hacks to me though.
@@ -136,10 +172,13 @@ public:
*/
// QList<Rule> rules;
- QList<VersionFilePtr> versionFiles;
- VersionFilePtr versionFile(const QString &id);
+ QList<VersionPatchPtr> VersionPatches;
+ VersionPatchPtr versionPatch(const QString &id);
+ VersionPatchPtr versionPatch(int index);
private:
+ QStringList m_externalPatches;
OneSixInstance *m_instance;
- QMap<QString, int> getExistingOrder() const;
+ void saveCurrentOrder() const;
+ int getFreeOrderNumber();
};
diff --git a/logic/minecraft/JarMod.cpp b/logic/minecraft/JarMod.cpp
new file mode 100644
index 00000000..18a9411c
--- /dev/null
+++ b/logic/minecraft/JarMod.cpp
@@ -0,0 +1,56 @@
+#include "JarMod.h"
+#include "logic/MMCJson.h"
+using namespace MMCJson;
+
+JarmodPtr Jarmod::fromJson(const QJsonObject &libObj, const QString &filename)
+{
+ JarmodPtr out(new Jarmod());
+ if (!libObj.contains("name"))
+ {
+ throw JSONValidationError(filename +
+ "contains a jarmod 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->baseurl);
+ readString("MMC-hint", out->hint);
+ readString("MMC-absoluteUrl", out->absoluteUrl);
+ if(!out->baseurl.isEmpty() && out->absoluteUrl.isEmpty())
+ {
+ out->absoluteUrl = out->baseurl + out->name;
+ }
+ return out;
+}
+
+QJsonObject Jarmod::toJson()
+{
+ QJsonObject out;
+ writeString(out, "name", name);
+ writeString(out, "url", baseurl);
+ writeString(out, "MMC-absoluteUrl", absoluteUrl);
+ writeString(out, "MMC-hint", hint);
+ return out;
+}
+
+QString Jarmod::url()
+{
+ if(!absoluteUrl.isEmpty())
+ return absoluteUrl;
+ else return baseurl + name;
+}
diff --git a/logic/minecraft/JarMod.h b/logic/minecraft/JarMod.h
new file mode 100644
index 00000000..c438dbcd
--- /dev/null
+++ b/logic/minecraft/JarMod.h
@@ -0,0 +1,18 @@
+#pragma once
+#include <QString>
+#include <QJsonObject>
+#include <memory>
+class Jarmod;
+typedef std::shared_ptr<Jarmod> JarmodPtr;
+class Jarmod
+{
+public: /* methods */
+ static JarmodPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
+ QString url();
+public: /* data */
+ QString name;
+ QString baseurl;
+ QString hint;
+ QString absoluteUrl;
+};
diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp
new file mode 100644
index 00000000..488a180a
--- /dev/null
+++ b/logic/minecraft/MinecraftVersion.cpp
@@ -0,0 +1,143 @@
+#include "MinecraftVersion.h"
+#include "InstanceVersion.h"
+#include "VersionBuildError.h"
+#include "VersionBuilder.h"
+
+bool MinecraftVersion::usesLegacyLauncher()
+{
+ return m_traits.contains("legacyLaunch") || m_traits.contains("aplhaLaunch");
+}
+
+QString MinecraftVersion::descriptor()
+{
+ return m_descriptor;
+}
+
+QString MinecraftVersion::name()
+{
+ return m_name;
+}
+
+QString MinecraftVersion::typeString() const
+{
+ if(m_type == "snapshot")
+ {
+ return QObject::tr("Snapshot");
+ }
+ else if (m_type == "release")
+ {
+ return QObject::tr("Regular release");
+ }
+ else if (m_type == "old_alpha")
+ {
+ return QObject::tr("Alpha");
+ }
+ else if (m_type == "old_beta")
+ {
+ return QObject::tr("Beta");
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+bool MinecraftVersion::hasJarMods()
+{
+ return false;
+}
+
+bool MinecraftVersion::isMinecraftVersion()
+{
+ return true;
+}
+
+// 1. assume the local file is good. load, check. If it's good, apply.
+// 2. if discrepancies are found, fall out and fail (impossible to apply incomplete version).
+void MinecraftVersion::applyFileTo(InstanceVersion *version)
+{
+ QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor));
+
+ auto versionObj = VersionBuilder::parseBinaryJsonFile(versionFile);
+ versionObj->applyTo(version);
+}
+
+void MinecraftVersion::applyTo(InstanceVersion *version)
+{
+ // do we have this one cached?
+ if (m_versionSource == Local)
+ {
+ applyFileTo(version);
+ return;
+ }
+ // if not builtin, do not proceed any further.
+ if (m_versionSource != Builtin)
+ {
+ throw VersionIncomplete(QObject::tr(
+ "Minecraft version %1 could not be applied: version files are missing.").arg(m_descriptor));
+ }
+ if (!m_descriptor.isNull())
+ {
+ version->id = m_descriptor;
+ }
+ if (!m_mainClass.isNull())
+ {
+ version->mainClass = m_mainClass;
+ }
+ if (!m_appletClass.isNull())
+ {
+ version->appletClass = m_appletClass;
+ }
+ if (!m_processArguments.isNull())
+ {
+ version->vanillaProcessArguments = m_processArguments;
+ version->processArguments = m_processArguments;
+ }
+ if (!m_type.isNull())
+ {
+ version->type = m_type;
+ }
+ if (!m_releaseTimeString.isNull())
+ {
+ version->m_releaseTimeString = m_releaseTimeString;
+ version->m_releaseTime = m_releaseTime;
+ }
+ if (!m_updateTimeString.isNull())
+ {
+ version->m_updateTimeString = m_updateTimeString;
+ version->m_updateTime = m_updateTime;
+ }
+ version->traits.unite(m_traits);
+}
+
+int MinecraftVersion::getOrder()
+{
+ return order;
+}
+
+void MinecraftVersion::setOrder(int order)
+{
+ this->order = order;
+}
+
+QList<JarmodPtr> MinecraftVersion::getJarMods()
+{
+ return QList<JarmodPtr>();
+}
+
+QString MinecraftVersion::getPatchName()
+{
+ return "Minecraft";
+}
+QString MinecraftVersion::getPatchVersion()
+{
+ return m_descriptor;
+}
+QString MinecraftVersion::getPatchID()
+{
+ return "net.minecraft";
+}
+QString MinecraftVersion::getPatchFilename()
+{
+ return QString();
+}
diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h
new file mode 100644
index 00000000..82073a97
--- /dev/null
+++ b/logic/minecraft/MinecraftVersion.h
@@ -0,0 +1,103 @@
+/* Copyright 2013 Andrew Okin
+ *
+ * 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 <QStringList>
+#include <QSet>
+#include <QDateTime>
+
+#include "logic/BaseVersion.h"
+#include "VersionPatch.h"
+#include "VersionFile.h"
+#include "VersionSource.h"
+
+class InstanceVersion;
+class MinecraftVersion;
+typedef std::shared_ptr<MinecraftVersion> MinecraftVersionPtr;
+
+class MinecraftVersion : public BaseVersion, public VersionPatch
+{
+public: /* methods */
+ bool usesLegacyLauncher();
+ virtual QString descriptor() override;
+ virtual QString name() override;
+ virtual QString typeString() const override;
+ virtual bool hasJarMods() override;
+ virtual bool isMinecraftVersion() override;
+ virtual void applyTo(InstanceVersion *version) override;
+ virtual int getOrder();
+ virtual void setOrder(int order);
+ virtual QList<JarmodPtr> getJarMods() override;
+ virtual QString getPatchID() override;
+ virtual QString getPatchVersion() override;
+ virtual QString getPatchName() override;
+ virtual QString getPatchFilename() override;
+ bool needsUpdate()
+ {
+ return m_versionSource == Remote;
+ }
+ bool hasUpdate()
+ {
+ return m_versionSource == Remote || (m_versionSource == Local && upstreamUpdate);
+ }
+
+private: /* methods */
+ void applyFileTo(InstanceVersion *version);
+
+public: /* data */
+ /// The URL that this version will be downloaded from. maybe.
+ QString download_url;
+
+ VersionSource m_versionSource = Builtin;
+
+ /// the human readable version name
+ QString m_name;
+
+ /// the version ID.
+ QString m_descriptor;
+
+ /// version traits. added by MultiMC
+ QSet<QString> m_traits;
+
+ /// The main class this version uses (if any, can be empty).
+ QString m_mainClass;
+
+ /// The applet class this version uses (if any, can be empty).
+ QString m_appletClass;
+
+ /// The process arguments used by this version
+ QString m_processArguments;
+
+ /// The type of this release
+ QString m_type;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
+ /// MD5 hash of the minecraft jar
+ QString m_jarChecksum;
+
+ /// order of this file... default = -2
+ int order = -2;
+
+ /// an update available from Mojang
+ MinecraftVersionPtr upstreamUpdate;
+};
diff --git a/logic/minecraft/MinecraftVersionList.cpp b/logic/minecraft/MinecraftVersionList.cpp
new file mode 100644
index 00000000..3aa1ac01
--- /dev/null
+++ b/logic/minecraft/MinecraftVersionList.cpp
@@ -0,0 +1,602 @@
+/* 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 <QtXml>
+#include "logic/MMCJson.h"
+#include <QtAlgorithms>
+#include <QtNetwork>
+
+#include "MultiMC.h"
+#include "MMCError.h"
+
+#include "MinecraftVersionList.h"
+#include "logic/net/URLConstants.h"
+
+#include "ParseUtils.h"
+#include "VersionBuilder.h"
+#include <logic/VersionFilterData.h>
+#include <pathutils.h>
+
+static const char * localVersionCache = "versions/versions.dat";
+
+class ListLoadError : public MMCError
+{
+public:
+ ListLoadError(QString cause) : MMCError(cause) {};
+ virtual ~ListLoadError() noexcept
+ {
+ }
+};
+
+MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent)
+{
+ loadBuiltinList();
+ loadCachedList();
+}
+
+Task *MinecraftVersionList::getLoadTask()
+{
+ return new MCVListLoadTask(this);
+}
+
+bool MinecraftVersionList::isLoaded()
+{
+ return m_loaded;
+}
+
+const BaseVersionPtr MinecraftVersionList::at(int i) const
+{
+ return m_vlist.at(i);
+}
+
+int MinecraftVersionList::count() const
+{
+ return m_vlist.count();
+}
+
+static bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second)
+{
+ auto left = std::dynamic_pointer_cast<MinecraftVersion>(first);
+ auto right = std::dynamic_pointer_cast<MinecraftVersion>(second);
+ return left->m_releaseTime > right->m_releaseTime;
+}
+
+void MinecraftVersionList::sortInternal()
+{
+ qSort(m_vlist.begin(), m_vlist.end(), cmpVersions);
+}
+
+void MinecraftVersionList::loadCachedList()
+{
+ QFile localIndex(localVersionCache);
+ if (!localIndex.exists())
+ {
+ return;
+ }
+ if (!localIndex.open(QIODevice::ReadOnly))
+ {
+ // FIXME: this is actually a very bad thing! How do we deal with this?
+ QLOG_ERROR() << "The minecraft version cache can't be read.";
+ return;
+ }
+ auto data = localIndex.readAll();
+ try
+ {
+ localIndex.close();
+ QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(data);
+ if (jsonDoc.isNull())
+ {
+ throw ListLoadError(tr("Error reading the version list."));
+ }
+ loadMojangList(jsonDoc, Local);
+ }
+ catch (MMCError &e)
+ {
+ // the cache has gone bad for some reason... flush it.
+ QLOG_ERROR() << "The minecraft version cache is corrupted. Flushing cache.";
+ localIndex.remove();
+ return;
+ }
+ m_hasLocalIndex = true;
+}
+
+void MinecraftVersionList::loadBuiltinList()
+{
+ QLOG_INFO() << "Loading builtin version list.";
+ // grab the version list data from internal resources.
+ QResource versionList(":/versions/minecraft.json");
+ QFile filez(versionList.absoluteFilePath());
+ filez.open(QIODevice::ReadOnly);
+ auto data = filez.readAll();
+
+ // parse the data as json
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ QJsonObject root = jsonDoc.object();
+
+ // parse all the versions
+ for (const auto version : MMCJson::ensureArray(root.value("versions")))
+ {
+ QJsonObject versionObj = version.toObject();
+ QString versionID = versionObj.value("id").toString("");
+ QString versionTypeStr = versionObj.value("type").toString("");
+ if (versionID.isEmpty() || versionTypeStr.isEmpty())
+ {
+ QLOG_ERROR() << "Parsed version is missing ID or type";
+ continue;
+ }
+
+ if (g_VersionFilterData.legacyBlacklist.contains(versionID))
+ {
+ QLOG_WARN() << "Blacklisted legacy version ignored: " << versionID;
+ continue;
+ }
+
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
+
+ // Parse the timestamp.
+ if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
+ mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid version timestamp";
+ continue;
+ }
+
+ // Get the download URL.
+ mcVersion->download_url =
+ "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
+
+ mcVersion->m_versionSource = Builtin;
+ mcVersion->m_type = versionTypeStr;
+ mcVersion->m_appletClass = versionObj.value("appletClass").toString("");
+ mcVersion->m_mainClass = versionObj.value("mainClass").toString("");
+ mcVersion->m_jarChecksum = versionObj.value("checksum").toString("");
+ mcVersion->m_processArguments = versionObj.value("processArguments").toString("legacy");
+ if (versionObj.contains("+traits"))
+ {
+ for (auto traitVal : MMCJson::ensureArray(versionObj.value("+traits")))
+ {
+ mcVersion->m_traits.insert(MMCJson::ensureString(traitVal));
+ }
+ }
+ m_lookup[versionID] = mcVersion;
+ m_vlist.append(mcVersion);
+ }
+}
+
+void MinecraftVersionList::loadMojangList(QJsonDocument jsonDoc, VersionSource source)
+{
+ QLOG_INFO() << "Loading" << ((source == Remote) ? "remote" : "local") << "version list.";
+
+ if (!jsonDoc.isObject())
+ {
+ throw ListLoadError(tr("Error parsing version list JSON: jsonDoc is not an object"));
+ }
+
+ QJsonObject root = jsonDoc.object();
+
+ try
+ {
+ QJsonObject latest = MMCJson::ensureObject(root.value("latest"));
+ m_latestReleaseID = MMCJson::ensureString(latest.value("release"));
+ m_latestSnapshotID = MMCJson::ensureString(latest.value("snapshot"));
+ }
+ catch (MMCError &err)
+ {
+ QLOG_ERROR()
+ << tr("Error parsing version list JSON: couldn't determine latest versions");
+ }
+
+ // Now, get the array of versions.
+ if (!root.value("versions").isArray())
+ {
+ throw ListLoadError(tr("Error parsing version list JSON: version list object is "
+ "missing 'versions' array"));
+ }
+ QJsonArray versions = root.value("versions").toArray();
+
+ QList<BaseVersionPtr> tempList;
+ for (auto version : versions)
+ {
+ // Load the version info.
+ if (!version.isObject())
+ {
+ QLOG_ERROR() << "Error while parsing version list : invalid JSON structure";
+ continue;
+ }
+
+ QJsonObject versionObj = version.toObject();
+ QString versionID = versionObj.value("id").toString("");
+ if (versionID.isEmpty())
+ {
+ QLOG_ERROR() << "Error while parsing version : version ID is missing";
+ continue;
+ }
+
+ if (g_VersionFilterData.legacyBlacklist.contains(versionID))
+ {
+ QLOG_WARN() << "Blacklisted legacy version ignored: " << versionID;
+ continue;
+ }
+
+ // Now, we construct the version object and add it to the list.
+ std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion());
+ mcVersion->m_name = mcVersion->m_descriptor = versionID;
+
+ if (!parse_timestamp(versionObj.value("releaseTime").toString(""),
+ mcVersion->m_releaseTimeString, mcVersion->m_releaseTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid release timestamp";
+ continue;
+ }
+ if (!parse_timestamp(versionObj.value("time").toString(""),
+ mcVersion->m_updateTimeString, mcVersion->m_updateTime))
+ {
+ QLOG_ERROR() << "Error while parsing version" << versionID
+ << ": invalid update timestamp";
+ continue;
+ }
+
+ if (mcVersion->m_releaseTime < g_VersionFilterData.legacyCutoffDate)
+ {
+ continue;
+ }
+
+ // depends on where we load the version from -- network request or local file?
+ mcVersion->m_versionSource = source;
+
+ QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/";
+ mcVersion->download_url = dlUrl;
+ QString versionTypeStr = versionObj.value("type").toString("");
+ if (versionTypeStr.isEmpty())
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ // OneSix or Legacy. use filter to determine type
+ if (versionTypeStr == "release")
+ {
+ }
+ else if (versionTypeStr == "snapshot") // It's a snapshot... yay
+ {
+ }
+ else if (versionTypeStr == "old_alpha")
+ {
+ }
+ else if (versionTypeStr == "old_beta")
+ {
+ }
+ else
+ {
+ // FIXME: log this somewhere
+ continue;
+ }
+ mcVersion->m_type = versionTypeStr;
+ tempList.append(mcVersion);
+ }
+ updateListData(tempList);
+ if(source == Remote)
+ {
+ m_loaded = true;
+ }
+}
+
+void MinecraftVersionList::sort()
+{
+ beginResetModel();
+ sortInternal();
+ endResetModel();
+}
+
+BaseVersionPtr MinecraftVersionList::getLatestStable() const
+{
+ if(m_lookup.contains(m_latestReleaseID))
+ return m_lookup[m_latestReleaseID];
+ return BaseVersionPtr();
+}
+
+void MinecraftVersionList::updateListData(QList<BaseVersionPtr> versions)
+{
+ beginResetModel();
+ for (auto version : versions)
+ {
+ auto descr = version->descriptor();
+
+ if (!m_lookup.contains(descr))
+ {
+ m_lookup[version->descriptor()] = version;
+ m_vlist.append(version);
+ continue;
+ }
+ auto orig = std::dynamic_pointer_cast<MinecraftVersion>(m_lookup[descr]);
+ auto added = std::dynamic_pointer_cast<MinecraftVersion>(version);
+ // updateListData is called after Mojang list loads. those can be local or remote
+ // remote comes always after local
+ // any other options are ignored
+ if (orig->m_versionSource != Local || added->m_versionSource != Remote)
+ {
+ continue;
+ }
+ // is it actually an update?
+ if (orig->m_updateTime >= added->m_updateTime)
+ {
+ // nope.
+ continue;
+ }
+ // alright, it's an update. put it inside the original, for further processing.
+ orig->upstreamUpdate = added;
+ }
+ sortInternal();
+ endResetModel();
+}
+
+inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname)
+{
+ QDomNodeList elementList = parent.elementsByTagName(tagname);
+ if (elementList.count())
+ return elementList.at(0).toElement();
+ else
+ return QDomElement();
+}
+
+MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist)
+{
+ m_list = vlist;
+ m_currentStable = NULL;
+ vlistReply = nullptr;
+}
+
+void MCVListLoadTask::executeTask()
+{
+ setStatus(tr("Loading instance version list..."));
+ auto worker = MMC->qnam();
+ vlistReply = worker->get(QNetworkRequest(
+ QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json")));
+ connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded()));
+}
+
+void MCVListLoadTask::list_downloaded()
+{
+ if (vlistReply->error() != QNetworkReply::NoError)
+ {
+ vlistReply->deleteLater();
+ emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString());
+ return;
+ }
+
+ auto data = vlistReply->readAll();
+ vlistReply->deleteLater();
+ try
+ {
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ throw ListLoadError(
+ tr("Error parsing version list JSON: %1").arg(jsonError.errorString()));
+ }
+ m_list->loadMojangList(jsonDoc, Remote);
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(e.cause());
+ return;
+ }
+
+ emitSucceeded();
+ return;
+}
+
+MCVListVersionUpdateTask::MCVListVersionUpdateTask(MinecraftVersionList *vlist,
+ QString updatedVersion)
+ : Task()
+{
+ m_list = vlist;
+ versionToUpdate = updatedVersion;
+}
+
+void MCVListVersionUpdateTask::executeTask()
+{
+ QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionToUpdate + "/" +
+ versionToUpdate + ".json";
+ auto job = new NetJob("Version index");
+ job->addNetAction(ByteArrayDownload::make(QUrl(urlstr)));
+ specificVersionDownloadJob.reset(job);
+ connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(json_downloaded()));
+ connect(specificVersionDownloadJob.get(), SIGNAL(failed(QString)), SIGNAL(failed(QString)));
+ connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)),
+ SIGNAL(progress(qint64, qint64)));
+ specificVersionDownloadJob->start();
+}
+
+void MCVListVersionUpdateTask::json_downloaded()
+{
+ NetActionPtr DlJob = specificVersionDownloadJob->first();
+ auto data = std::dynamic_pointer_cast<ByteArrayDownload>(DlJob)->m_data;
+ specificVersionDownloadJob.reset();
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ emitFailed(tr("The download version file is not valid."));
+ return;
+ }
+ VersionFilePtr file;
+ try
+ {
+ file = VersionFile::fromJson(jsonDoc, "net.minecraft.json", false);
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(tr("Couldn't process version file: %1").arg(e.cause()));
+ return;
+ }
+ QList<RawLibraryPtr> filteredLibs;
+ QList<RawLibraryPtr> lwjglLibs;
+ QSet<QString> lwjglFilter = {
+ "net.java.jinput:jinput", "net.java.jinput:jinput-platform",
+ "net.java.jutils:jutils", "org.lwjgl.lwjgl:lwjgl",
+ "org.lwjgl.lwjgl:lwjgl_util", "org.lwjgl.lwjgl:lwjgl-platform"};
+ for (auto lib : file->overwriteLibs)
+ {
+ if (lwjglFilter.contains(lib->fullname()))
+ {
+ lwjglLibs.append(lib);
+ }
+ else
+ {
+ filteredLibs.append(lib);
+ }
+ }
+ file->overwriteLibs = filteredLibs;
+
+ // TODO: recognize and add LWJGL versions here.
+
+ file->fileId = "net.minecraft";
+
+ // now dump the file to disk
+ auto doc = file->toJson(false);
+ auto newdata = doc.toBinaryData();
+ QLOG_INFO() << newdata;
+ QString targetPath = "versions/" + versionToUpdate + "/" + versionToUpdate + ".dat";
+ ensureFilePathExists(targetPath);
+ QSaveFile vfile1(targetPath);
+ if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly))
+ {
+ emitFailed(tr("Can't open %1 for writing.").arg(targetPath));
+ return;
+ }
+ qint64 actual = 0;
+ if ((actual = vfile1.write(newdata)) != newdata.size())
+ {
+ emitFailed(tr("Failed to write into %1. Written %2 out of %3.")
+ .arg(targetPath)
+ .arg(actual)
+ .arg(newdata.size()));
+ return;
+ }
+ if (!vfile1.commit())
+ {
+ emitFailed(tr("Can't commit changes to %1").arg(targetPath));
+ return;
+ }
+
+ m_list->finalizeUpdate(versionToUpdate);
+ emitSucceeded();
+}
+
+std::shared_ptr<Task> MinecraftVersionList::createUpdateTask(QString version)
+{
+ return std::shared_ptr<Task>(new MCVListVersionUpdateTask(this, version));
+}
+
+void MinecraftVersionList::saveCachedList()
+{
+ // FIXME: throw.
+ if (!ensureFilePathExists(localVersionCache))
+ return;
+ QSaveFile tfile(localVersionCache);
+ if (!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ return;
+ QJsonObject toplevel;
+ QJsonArray entriesArr;
+ for (auto version : m_vlist)
+ {
+ auto mcversion = std::dynamic_pointer_cast<MinecraftVersion>(version);
+ // do not save the remote versions.
+ if (mcversion->m_versionSource != Local)
+ continue;
+ QJsonObject entryObj;
+
+ entryObj.insert("id", mcversion->descriptor());
+ entryObj.insert("time", mcversion->m_updateTimeString);
+ entryObj.insert("releaseTime", mcversion->m_releaseTimeString);
+ entryObj.insert("type", mcversion->m_type);
+ entriesArr.append(entryObj);
+ }
+ toplevel.insert("versions", entriesArr);
+
+ {
+ bool someLatest = false;
+ QJsonObject latestObj;
+ if(!m_latestReleaseID.isNull())
+ {
+ latestObj.insert("release", m_latestReleaseID);
+ someLatest = true;
+ }
+ if(!m_latestSnapshotID.isNull())
+ {
+ latestObj.insert("snapshot", m_latestSnapshotID);
+ someLatest = true;
+ }
+ if(someLatest)
+ {
+ toplevel.insert("latest", latestObj);
+ }
+ }
+
+ QJsonDocument doc(toplevel);
+ QByteArray jsonData = doc.toBinaryData();
+ qint64 result = tfile.write(jsonData);
+ if (result == -1)
+ return;
+ if (result != jsonData.size())
+ return;
+ tfile.commit();
+}
+
+void MinecraftVersionList::finalizeUpdate(QString version)
+{
+ int idx = -1;
+ for (int i = 0; i < m_vlist.size(); i++)
+ {
+ if (version == m_vlist[i]->descriptor())
+ {
+ idx = i;
+ break;
+ }
+ }
+ if (idx == -1)
+ {
+ return;
+ }
+
+ auto updatedVersion = std::dynamic_pointer_cast<MinecraftVersion>(m_vlist[idx]);
+
+ if (updatedVersion->m_versionSource == Builtin)
+ return;
+
+ if (updatedVersion->upstreamUpdate)
+ {
+ auto updatedWith = updatedVersion->upstreamUpdate;
+ updatedWith->m_versionSource = Local;
+ m_vlist[idx] = updatedWith;
+ m_lookup[version] = updatedWith;
+ }
+ else
+ {
+ updatedVersion->m_versionSource = Local;
+ }
+
+ dataChanged(index(idx), index(idx));
+
+ saveCachedList();
+}
diff --git a/logic/lists/MinecraftVersionList.h b/logic/minecraft/MinecraftVersionList.h
index 167f4d11..4753ce05 100644
--- a/logic/lists/MinecraftVersionList.h
+++ b/logic/minecraft/MinecraftVersionList.h
@@ -19,11 +19,13 @@
#include <QList>
#include <QSet>
-#include "BaseVersionList.h"
+#include "logic/BaseVersionList.h"
#include "logic/tasks/Task.h"
-#include "logic/MinecraftVersion.h"
+#include "logic/minecraft/MinecraftVersion.h"
+#include <logic/net/NetJob.h>
class MCVListLoadTask;
+class MCVListVersionUpdateTask;
class QNetworkReply;
class MinecraftVersionList : public BaseVersionList
@@ -31,11 +33,19 @@ class MinecraftVersionList : public BaseVersionList
Q_OBJECT
private:
void sortInternal();
+ void loadBuiltinList();
+ void loadMojangList(QJsonDocument jsonDoc, VersionSource source);
+ void loadCachedList();
+ void saveCachedList();
+ void finalizeUpdate(QString version);
public:
friend class MCVListLoadTask;
+ friend class MCVListVersionUpdateTask;
explicit MinecraftVersionList(QObject *parent = 0);
+ std::shared_ptr<Task> createUpdateTask(QString version);
+
virtual Task *getLoadTask();
virtual bool isLoaded();
virtual const BaseVersionPtr at(int i) const;
@@ -46,8 +56,12 @@ public:
protected:
QList<BaseVersionPtr> m_vlist;
+ QMap<QString, BaseVersionPtr> m_lookup;
bool m_loaded = false;
+ bool m_hasLocalIndex = false;
+ QString m_latestReleaseID = "INVALID";
+ QString m_latestSnapshotID = "INVALID";
protected
slots:
@@ -60,9 +74,9 @@ class MCVListLoadTask : public Task
public:
explicit MCVListLoadTask(MinecraftVersionList *vlist);
- ~MCVListLoadTask();
+ virtual ~MCVListLoadTask() override{};
- virtual void executeTask();
+ virtual void executeTask() override;
protected
slots:
@@ -72,5 +86,23 @@ protected:
QNetworkReply *vlistReply;
MinecraftVersionList *m_list;
MinecraftVersion *m_currentStable;
- QSet<QString> legacyWhitelist;
+};
+
+class MCVListVersionUpdateTask : public Task
+{
+ Q_OBJECT
+
+public:
+ explicit MCVListVersionUpdateTask(MinecraftVersionList *vlist, QString updatedVersion);
+ virtual ~MCVListVersionUpdateTask() override{};
+ virtual void executeTask() override;
+
+protected
+slots:
+ void json_downloaded();
+
+protected:
+ NetJobPtr specificVersionDownloadJob;
+ QString versionToUpdate;
+ MinecraftVersionList *m_list;
};
diff --git a/logic/OneSixLibrary.cpp b/logic/minecraft/OneSixLibrary.cpp
index 45fa169e..7f69d9f8 100644
--- a/logic/OneSixLibrary.cpp
+++ b/logic/minecraft/OneSixLibrary.cpp
@@ -23,6 +23,23 @@
#include <JlCompress.h>
#include "logger/QsLog.h"
+OneSixLibrary::OneSixLibrary(RawLibraryPtr base)
+{
+ m_name = base->m_name;
+ m_base_url = base->m_base_url;
+ m_hint = base->m_hint;
+ m_absolute_url = base->m_absolute_url;
+ extract_excludes = base->extract_excludes;
+ m_native_suffixes = base->m_native_suffixes;
+ m_rules = base->m_rules;
+ finalize();
+}
+
+OneSixLibraryPtr OneSixLibrary::fromRawLibrary(RawLibraryPtr lib)
+{
+ return OneSixLibraryPtr(new OneSixLibrary(lib));
+}
+
void OneSixLibrary::finalize()
{
QStringList parts = m_name.split(':');
@@ -30,7 +47,7 @@ void OneSixLibrary::finalize()
relative.replace('.', '/');
relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2];
- if (!m_is_native)
+ if (!isNative())
relative += ".jar";
else
{
@@ -65,7 +82,7 @@ void OneSixLibrary::finalize()
}
m_is_active = (result == Allow);
}
- if (m_is_native)
+ if (isNative())
{
m_is_active = m_is_active && m_native_suffixes.contains(currentSystem);
m_decenttype = "Native";
@@ -84,13 +101,8 @@ void OneSixLibrary::setBaseUrl(const QString &base_url)
{
m_base_url = base_url;
}
-void OneSixLibrary::setIsNative()
-{
- m_is_native = true;
-}
void OneSixLibrary::addNative(OpSys os, const QString &suffix)
{
- m_is_native = true;
m_native_suffixes[os] = suffix;
}
void OneSixLibrary::clearSuffixes()
@@ -105,10 +117,6 @@ bool OneSixLibrary::isActive() const
{
return m_is_active;
}
-bool OneSixLibrary::isNative() const
-{
- return m_is_native;
-}
QString OneSixLibrary::downloadUrl() const
{
if (m_absolute_url.size())
@@ -223,52 +231,3 @@ bool OneSixLibrary::extractTo(QString target_dir)
}
return true;
}
-
-QJsonObject OneSixLibrary::toJson()
-{
- QJsonObject libRoot;
- libRoot.insert("name", m_name);
- if (m_absolute_url.size())
- libRoot.insert("MMC-absoluteUrl", m_absolute_url);
- if (m_hint.size())
- 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.isEmpty())
- {
- libRoot.insert("url", m_base_url);
- }
- if (isNative() && m_native_suffixes.size())
- {
- QJsonObject nativeList;
- auto iter = m_native_suffixes.begin();
- while (iter != m_native_suffixes.end())
- {
- nativeList.insert(OpSys_toString(iter.key()), iter.value());
- iter++;
- }
- libRoot.insert("natives", nativeList);
- }
- if (isNative() && extract_excludes.size())
- {
- QJsonArray excludes;
- QJsonObject extract;
- for (auto exclude : extract_excludes)
- {
- excludes.append(exclude);
- }
- extract.insert("exclude", excludes);
- libRoot.insert("extract", extract);
- }
- if (m_rules.size())
- {
- QJsonArray allRules;
- for (auto &rule : m_rules)
- {
- QJsonObject ruleObj = rule->toJson();
- allRules.append(ruleObj);
- }
- libRoot.insert("rules", allRules);
- }
- return libRoot;
-}
diff --git a/logic/OneSixLibrary.h b/logic/minecraft/OneSixLibrary.h
index 61d4c8e2..3d38985b 100644
--- a/logic/OneSixLibrary.h
+++ b/logic/minecraft/OneSixLibrary.h
@@ -23,28 +23,17 @@
#include <memory>
#include "logic/net/URLConstants.h"
-#include "OpSys.h"
+#include "logic/minecraft/OpSys.h"
+#include "logic/minecraft/RawLibrary.h"
class Rule;
class OneSixLibrary;
typedef std::shared_ptr<OneSixLibrary> OneSixLibraryPtr;
-class OneSixLibrary
+class OneSixLibrary : public RawLibrary
{
private:
- // basic values used internally (so far)
- QString m_name;
- QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
- QList<std::shared_ptr<Rule>> m_rules;
-
- // custom values
- /// absolute URL. takes precedence over m_download_path, if defined
- QString m_absolute_url;
- /// download hint - how to actually get the library
- QString m_hint;
-
- // derived values used for real things
/// a decent name fit for display
QString m_decentname;
/// a decent version fit for display
@@ -57,13 +46,9 @@ private:
QString m_download_url;
/// is this lib actually active on the current OS?
bool m_is_active = false;
- /// is the library a native?
- bool m_is_native = false;
- /// native suffixes per OS
- QMap<OpSys, QString> m_native_suffixes;
public:
- QStringList extract_excludes;
+
QString minVersion;
enum DependType
@@ -80,15 +65,16 @@ public:
m_name = name;
dependType = type;
}
-
+ /// Constructor
+ OneSixLibrary(RawLibraryPtr base);
+ static OneSixLibraryPtr fromRawLibrary(RawLibraryPtr lib);
+
/// Returns the raw name field
QString rawName() const
{
return m_name;
}
- QJsonObject toJson();
-
/**
* finalize the library, processing the input values into derived values and state
*
@@ -116,8 +102,6 @@ public:
/// Set the url base for downloads
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, const QString &suffix);
/// Clears all suffixes
@@ -127,8 +111,6 @@ public:
/// Returns true if the library should be loaded (or extracted, in case of natives)
bool isActive() const;
- /// Returns true if the library is native
- bool isNative() const;
/// Get the URL to download the library from
QString downloadUrl() const;
/// Get the relative path where the library should be saved
diff --git a/logic/OneSixRule.cpp b/logic/minecraft/OneSixRule.cpp
index d8d13b50..93c49e5e 100644
--- a/logic/OneSixRule.cpp
+++ b/logic/minecraft/OneSixRule.cpp
@@ -18,6 +18,15 @@
#include "OneSixRule.h"
+RuleAction RuleAction_fromString(QString name)
+{
+ if (name == "allow")
+ return Allow;
+ if (name == "disallow")
+ return Disallow;
+ return Defer;
+}
+
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules)
{
QList<std::shared_ptr<Rule>> rules;
@@ -79,11 +88,3 @@ QJsonObject OsRule::toJson()
return ruleObj;
}
-RuleAction RuleAction_fromString(QString name)
-{
- if (name == "allow")
- return Allow;
- if (name == "disallow")
- return Disallow;
- return Defer;
-}
diff --git a/logic/OneSixRule.h b/logic/minecraft/OneSixRule.h
index 426e2886..a18093b0 100644
--- a/logic/OneSixRule.h
+++ b/logic/minecraft/OneSixRule.h
@@ -16,8 +16,13 @@
#pragma once
#include <QString>
+#include <QList>
+#include <QJsonObject>
+#include <memory>
+#include "OpSys.h"
-#include "logic/OneSixLibrary.h"
+class OneSixLibrary;
+class Rule;
enum RuleAction
{
@@ -26,7 +31,6 @@ enum RuleAction
Defer
};
-RuleAction RuleAction_fromString(QString);
QList<std::shared_ptr<Rule>> rulesFromJsonV4(const QJsonObject &objectWithRules);
class Rule
diff --git a/logic/OpSys.cpp b/logic/minecraft/OpSys.cpp
index e001b7f3..e001b7f3 100644
--- a/logic/OpSys.cpp
+++ b/logic/minecraft/OpSys.cpp
diff --git a/logic/OpSys.h b/logic/minecraft/OpSys.h
index 363c87d7..363c87d7 100644
--- a/logic/OpSys.h
+++ b/logic/minecraft/OpSys.h
diff --git a/logic/minecraft/ParseUtils.cpp b/logic/minecraft/ParseUtils.cpp
new file mode 100644
index 00000000..f94de6ff
--- /dev/null
+++ b/logic/minecraft/ParseUtils.cpp
@@ -0,0 +1,24 @@
+#include <QDateTime>
+#include <QString>
+#include "ParseUtils.h"
+#include <logic/MMCJson.h>
+
+QDateTime timeFromS3Time(QString str)
+{
+ return QDateTime::fromString(str, Qt::ISODate);
+}
+
+bool parse_timestamp (const QString & raw, QString & save_here, QDateTime & parse_here)
+{
+ save_here = raw;
+ if (save_here.isEmpty())
+ {
+ return false;
+ }
+ parse_here = timeFromS3Time(save_here);
+ if (!parse_here.isValid())
+ {
+ return false;
+ }
+ return true;
+}
diff --git a/logic/minecraft/ParseUtils.h b/logic/minecraft/ParseUtils.h
new file mode 100644
index 00000000..bd2f6ffb
--- /dev/null
+++ b/logic/minecraft/ParseUtils.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <QString>
+#include <QDateTime>
+
+/**
+ * parse the S3 timestamp in 'raw' and fill the forwarded variables.
+ * return true/false for success/failure
+ */
+bool parse_timestamp (const QString &raw, QString &save_here, QDateTime &parse_here);
+
+/**
+ * take the timestamp used by S3 and turn it into QDateTime
+ */
+QDateTime timeFromS3Time(QString str);
diff --git a/logic/minecraft/RawLibrary.cpp b/logic/minecraft/RawLibrary.cpp
new file mode 100644
index 00000000..7e0ebff0
--- /dev/null
+++ b/logic/minecraft/RawLibrary.cpp
@@ -0,0 +1,205 @@
+#include "logic/MMCJson.h"
+using namespace MMCJson;
+
+#include "RawLibrary.h"
+
+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->m_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->m_base_url);
+ readString("MMC-hint", out->m_hint);
+ readString("MMC-absulute_url", out->m_absolute_url);
+ readString("MMC-absoluteUrl", out->m_absolute_url);
+ if (libObj.contains("extract"))
+ {
+ out->applyExcludes = true;
+ auto extractObj = ensureObject(libObj.value("extract"));
+ for (auto excludeVal : ensureArray(extractObj.value("exclude")))
+ {
+ out->extract_excludes.append(ensureString(excludeVal));
+ }
+ }
+ if (libObj.contains("natives"))
+ {
+ 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->m_native_suffixes[opSys] = it.value().toString();
+ }
+ }
+ }
+ if (libObj.contains("rules"))
+ {
+ out->applyRules = true;
+ out->m_rules = rulesFromJsonV4(libObj);
+ }
+ return out;
+}
+
+RawLibraryPtr RawLibrary::fromJsonPlus(const QJsonObject &libObj, const QString &filename)
+{
+ auto lib = RawLibrary::fromJson(libObj, filename);
+ if (libObj.contains("insert"))
+ {
+ QJsonValue insertVal = ensureExists(libObj.value("insert"), "library insert rule");
+ 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::Append;
+ }
+ 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");
+ }
+ }
+ return lib;
+}
+
+bool RawLibrary::isNative() const
+{
+ return m_native_suffixes.size() != 0;
+}
+
+QJsonObject RawLibrary::toJson()
+{
+ QJsonObject libRoot;
+ libRoot.insert("name", m_name);
+ if (m_absolute_url.size())
+ libRoot.insert("MMC-absoluteUrl", m_absolute_url);
+ if (m_hint.size())
+ 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.isEmpty())
+ {
+ libRoot.insert("url", m_base_url);
+ }
+ if (isNative())
+ {
+ QJsonObject nativeList;
+ auto iter = m_native_suffixes.begin();
+ while (iter != m_native_suffixes.end())
+ {
+ nativeList.insert(OpSys_toString(iter.key()), iter.value());
+ iter++;
+ }
+ libRoot.insert("natives", nativeList);
+ if (extract_excludes.size())
+ {
+ QJsonArray excludes;
+ QJsonObject extract;
+ for (auto exclude : extract_excludes)
+ {
+ excludes.append(exclude);
+ }
+ extract.insert("exclude", excludes);
+ libRoot.insert("extract", extract);
+ }
+ }
+ if (m_rules.size())
+ {
+ QJsonArray allRules;
+ for (auto &rule : m_rules)
+ {
+ QJsonObject ruleObj = rule->toJson();
+ allRules.append(ruleObj);
+ }
+ libRoot.insert("rules", allRules);
+ }
+ return libRoot;
+}
+
+QString RawLibrary::fullname()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0] + ":" + parts[1];
+}
+
+QString RawLibrary::group()
+{
+ QStringList parts = m_name.split(':');
+ return parts[0];
+}
+
+QString RawLibrary::version()
+{
+ QStringList parts = m_name.split(':');
+ return parts[2];
+}
diff --git a/logic/minecraft/RawLibrary.h b/logic/minecraft/RawLibrary.h
new file mode 100644
index 00000000..f5a28c61
--- /dev/null
+++ b/logic/minecraft/RawLibrary.h
@@ -0,0 +1,64 @@
+#pragma once
+#include <QString>
+#include <QPair>
+#include <QList>
+#include <QStringList>
+#include <QMap>
+#include <memory>
+
+#include "logic/minecraft/OneSixRule.h"
+#include "logic/minecraft/OpSys.h"
+#include "logic/net/URLConstants.h"
+
+class RawLibrary;
+typedef std::shared_ptr<RawLibrary> RawLibraryPtr;
+
+class RawLibrary
+{
+public: /* methods */
+ /// read and create a basic library
+ static RawLibraryPtr fromJson(const QJsonObject &libObj, const QString &filename);
+ /// read and create a MultiMC '+' library. Those have some extra fields.
+ static RawLibraryPtr fromJsonPlus(const QJsonObject &libObj, const QString &filename);
+ QJsonObject toJson();
+
+ QString fullname();
+ QString version();
+ QString group();
+
+public: /* data */
+ QString m_name;
+ QString m_base_url = "https://" + URLConstants::LIBRARY_BASE;
+ /// type hint - modifies how the library is treated
+ QString m_hint;
+ /// absolute URL. takes precedence over m_download_path, if defined
+ QString m_absolute_url;
+
+ bool applyExcludes = false;
+ QStringList extract_excludes;
+
+ /// Returns true if the library is native
+ bool isNative() const;
+ /// native suffixes per OS
+ QMap<OpSys, QString> m_native_suffixes;
+
+ bool applyRules = false;
+ QList<std::shared_ptr<Rule>> m_rules;
+
+ // used for '+' libraries
+ enum InsertType
+ {
+ Apply,
+ Append,
+ Prepend,
+ Replace
+ } insertType = Append;
+ QString insertData;
+
+ // soft or hard dependency? hard means 'needs equal', soft means 'needs equal or newer'
+ enum DependType
+ {
+ Soft,
+ Hard
+ } dependType = Soft;
+};
diff --git a/logic/minecraft/VersionBuildError.h b/logic/minecraft/VersionBuildError.h
new file mode 100644
index 00000000..ae479851
--- /dev/null
+++ b/logic/minecraft/VersionBuildError.h
@@ -0,0 +1,58 @@
+#include "MMCError.h"
+
+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
+ {
+ }
+};
+
+/**
+ * files required for the version are not (yet?) present
+ */
+class VersionIncomplete : public VersionBuildError
+{
+public:
+ VersionIncomplete(QString missingPatch)
+ : VersionBuildError(QObject::tr("Version is incomplete: missing %1.")
+ .arg(missingPatch)) {};
+ virtual ~VersionIncomplete() noexcept
+ {
+ }
+}; \ No newline at end of file
diff --git a/logic/minecraft/VersionBuilder.cpp b/logic/minecraft/VersionBuilder.cpp
new file mode 100644
index 00000000..ddcf6333
--- /dev/null
+++ b/logic/minecraft/VersionBuilder.cpp
@@ -0,0 +1,349 @@
+/* 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 <QList>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QFile>
+#include <QFileInfo>
+#include <QMessageBox>
+#include <QObject>
+#include <QDir>
+#include <QDebug>
+#include <qresource.h>
+#include <modutils.h>
+
+#include "MultiMC.h"
+#include "logic/minecraft/VersionBuilder.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/OneSixRule.h"
+#include "logic/minecraft/VersionPatch.h"
+#include "logic/minecraft/VersionFile.h"
+#include "VersionBuildError.h"
+#include "MinecraftVersionList.h"
+
+#include "logic/OneSixInstance.h"
+#include "logic/MMCJson.h"
+
+#include "logger/QsLog.h"
+
+VersionBuilder::VersionBuilder()
+{
+}
+
+void VersionBuilder::build(InstanceVersion *version, OneSixInstance *instance,
+ const QStringList &external)
+{
+ VersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = instance;
+ builder.external_patches = external;
+ builder.buildInternal();
+}
+
+void VersionBuilder::readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj)
+{
+ VersionBuilder builder;
+ builder.m_version = version;
+ builder.m_instance = 0;
+ builder.readJsonAndApply(obj);
+}
+
+void VersionBuilder::buildFromCustomJson()
+{
+ QLOG_INFO() << "Building version from custom.json within the instance.";
+ QLOG_INFO() << "Reading custom.json";
+ auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("custom.json")), false);
+ file->name = "custom.json";
+ file->filename = "custom.json";
+ file->fileId = "org.multimc.custom.json";
+ file->order = -1;
+ file->version = QString();
+ m_version->VersionPatches.append(file);
+ m_version->finalize();
+ return;
+}
+
+void VersionBuilder::buildFromVersionJson()
+{
+ QLOG_INFO() << "Building version from version.json and patches within the instance.";
+ QLOG_INFO() << "Reading version.json";
+ auto file = parseJsonFile(QFileInfo(instance_root.absoluteFilePath("version.json")), false);
+ file->name = "Minecraft";
+ file->fileId = "org.multimc.version.json";
+ file->order = -1;
+ file->version = m_instance->intendedVersionId();
+ file->mcVersion = m_instance->intendedVersionId();
+ m_version->VersionPatches.append(file);
+
+ // load all patches, put into map for ordering, apply in the right order
+ readInstancePatches();
+
+ // some final touches
+ m_version->finalize();
+}
+
+void VersionBuilder::readInstancePatches()
+{
+ PatchOrder userOrder;
+ readOverrideOrders(m_instance, userOrder);
+ QDir patches(instance_root.absoluteFilePath("patches/"));
+
+ // first, load things by sort order.
+ for (auto id : userOrder)
+ {
+ // ignore builtins
+ if (id == "net.minecraft")
+ continue;
+ if (id == "org.lwjgl")
+ continue;
+ // parse the file
+ QString filename = patches.absoluteFilePath(id + ".json");
+ QLOG_INFO() << "Reading" << filename << "by user order";
+ auto file = parseJsonFile(QFileInfo(filename), false);
+ // sanity check. prevent tampering with files.
+ if (file->fileId != id)
+ {
+ throw VersionBuildError(
+ QObject::tr("load id %1 does not match internal id %2").arg(id, file->fileId));
+ }
+ m_version->VersionPatches.append(file);
+ }
+ // now load the rest by internal preference.
+ QMap<int, QPair<QString, VersionFilePtr>> files;
+ for (auto info : patches.entryInfoList(QStringList() << "*.json", QDir::Files))
+ {
+ // parse the file
+ QLOG_INFO() << "Reading" << info.fileName();
+ auto file = parseJsonFile(info, true);
+ // ignore builtins
+ if (file->fileId == "net.minecraft")
+ continue;
+ if (file->fileId == "org.lwjgl")
+ continue;
+ // do not load what we already loaded in the first pass
+ if (userOrder.contains(file->fileId))
+ continue;
+ if (files.contains(file->order))
+ {
+ // FIXME: do not throw?
+ 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));
+ }
+ for (auto order : files.keys())
+ {
+ auto &filePair = files[order];
+ m_version->VersionPatches.append(filePair.second);
+ }
+}
+
+void VersionBuilder::buildFromExternalPatches()
+{
+ QLOG_INFO() << "Building version from external files.";
+ int externalOrder = -1;
+ for (auto fileName : external_patches)
+ {
+ 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->order = (externalOrder += 1);
+ file->version = QString();
+ file->mcVersion = QString();
+ m_version->VersionPatches.append(file);
+ }
+ // some final touches
+ m_version->finalize();
+}
+
+void VersionBuilder::buildFromMultilayer()
+{
+ QLOG_INFO() << "Building version from multilayered sources.";
+ // just the builtin stuff for now
+ auto minecraftList = MMC->minecraftlist();
+ auto mcversion = minecraftList->findVersion(m_instance->intendedVersionId());
+ auto minecraftPatch = std::dynamic_pointer_cast<VersionPatch>(mcversion);
+ if (!minecraftPatch)
+ {
+ throw VersionIncomplete("net.minecraft");
+ }
+ minecraftPatch->setOrder(-2);
+ m_version->VersionPatches.append(minecraftPatch);
+
+ // TODO: this is obviously fake.
+ QResource LWJGL(":/versions/LWJGL/2.9.1.json");
+ auto lwjgl = parseJsonFile(LWJGL.absoluteFilePath(), false, false);
+ auto lwjglPatch = std::dynamic_pointer_cast<VersionPatch>(lwjgl);
+ if (!lwjglPatch)
+ {
+ throw VersionIncomplete("org.lwjgl");
+ }
+ lwjglPatch->setOrder(-1);
+ m_version->VersionPatches.append(lwjglPatch);
+
+ // load all patches, put into map for ordering, apply in the right order
+ readInstancePatches();
+
+ m_version->finalize();
+}
+
+void VersionBuilder::buildInternal()
+{
+ m_version->VersionPatches.clear();
+ instance_root = QDir(m_instance->instanceRoot());
+ // if we do external files, do just those.
+ if (!external_patches.isEmpty())
+ {
+ buildFromExternalPatches();
+ }
+ // else, if there's custom json, we just do that.
+ else if (QFile::exists(instance_root.absoluteFilePath("custom.json")))
+ {
+ buildFromCustomJson();
+ }
+ // version.json -> patches/*.json
+ else if (QFile::exists(instance_root.absoluteFilePath("version.json")))
+ {
+ buildFromVersionJson();
+ }
+ else
+ {
+ buildFromMultilayer();
+ }
+}
+
+void VersionBuilder::readJsonAndApply(const QJsonObject &obj)
+{
+ m_version->clear();
+
+ auto file = VersionFile::fromJson(QJsonDocument(obj), QString(), false);
+
+ file->applyTo(m_version);
+ m_version->VersionPatches.append(file);
+}
+
+VersionFilePtr VersionBuilder::parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder,
+ bool isFTB)
+{
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ 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)
+ {
+ throw JSONValidationError(
+ QObject::tr("Unable to process the version file %1: %2 at %3.")
+ .arg(fileInfo.fileName(), error.errorString())
+ .arg(error.offset));
+ }
+ return VersionFile::fromJson(doc, file.fileName(), requireOrder, isFTB);
+}
+
+VersionFilePtr VersionBuilder::parseBinaryJsonFile(const QFileInfo &fileInfo)
+{
+ QFile file(fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly))
+ {
+ throw JSONValidationError(QObject::tr("Unable to open the version file %1: %2.")
+ .arg(fileInfo.fileName(), file.errorString()));
+ }
+ QJsonDocument doc = QJsonDocument::fromBinaryData(file.readAll());
+ file.close();
+ if (doc.isNull())
+ {
+ file.remove();
+ throw JSONValidationError(
+ QObject::tr("Unable to process the version file %1.").arg(fileInfo.fileName()));
+ }
+ return VersionFile::fromJson(doc, file.fileName(), false, false);
+}
+
+static const int currentOrderFileVersion = 1;
+
+bool VersionBuilder::readOverrideOrders(OneSixInstance *instance, PatchOrder &order)
+{
+ 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";
+ return false;
+ }
+
+ // 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 false;
+ }
+
+ // and then read it and process it if all above is true.
+ try
+ {
+ auto obj = MMCJson::ensureObject(doc);
+ // check order file version.
+ auto version = MMCJson::ensureInteger(obj.value("version"), "version");
+ if (version != currentOrderFileVersion)
+ {
+ throw JSONValidationError(QObject::tr("Invalid order file version, expected %1")
+ .arg(currentOrderFileVersion));
+ }
+ auto orderArray = MMCJson::ensureArray(obj.value("order"));
+ for(auto item: orderArray)
+ {
+ order.append(MMCJson::ensureString(item));
+ }
+ }
+ catch (JSONValidationError &err)
+ {
+ QLOG_ERROR() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
+ QLOG_WARN() << "Ignoring overriden order";
+ order.clear();
+ return false;
+ }
+ return true;
+}
+
+bool VersionBuilder::writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order)
+{
+ QJsonObject obj;
+ obj.insert("version", currentOrderFileVersion);
+ QJsonArray orderArray;
+ for(auto str: order)
+ {
+ orderArray.append(str);
+ }
+ obj.insert("order", orderArray);
+ QFile orderFile(instance->instanceRoot() + "/order.json");
+ if (!orderFile.open(QFile::WriteOnly))
+ {
+ QLOG_ERROR() << "Couldn't open" << orderFile.fileName()
+ << "for writing:" << orderFile.errorString();
+ return false;
+ }
+ orderFile.write(QJsonDocument(obj).toJson(QJsonDocument::Indented));
+ return true;
+}
diff --git a/logic/minecraft/VersionBuilder.h b/logic/minecraft/VersionBuilder.h
new file mode 100644
index 00000000..350179b9
--- /dev/null
+++ b/logic/minecraft/VersionBuilder.h
@@ -0,0 +1,56 @@
+/* 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>
+#include <QMap>
+#include "VersionFile.h"
+
+class InstanceVersion;
+class OneSixInstance;
+class QJsonObject;
+class QFileInfo;
+
+typedef QStringList PatchOrder;
+
+class VersionBuilder
+{
+ VersionBuilder();
+public:
+ static void build(InstanceVersion *version, OneSixInstance *instance, const QStringList &external);
+ static void readJsonAndApplyToVersion(InstanceVersion *version, const QJsonObject &obj);
+ static VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder, bool isFTB = false);
+ static VersionFilePtr parseBinaryJsonFile(const QFileInfo &fileInfo);
+
+ bool readOverrideOrders(OneSixInstance *instance, PatchOrder &order);
+ static bool writeOverrideOrders(OneSixInstance *instance, const PatchOrder &order);
+
+private:
+ InstanceVersion *m_version;
+ OneSixInstance *m_instance;
+ QStringList external_patches;
+ QDir instance_root;
+
+ void buildInternal();
+ void buildFromExternalPatches();
+ void buildFromCustomJson();
+ void buildFromVersionJson();
+ void buildFromMultilayer();
+
+ void readInstancePatches();
+
+ void readJsonAndApply(const QJsonObject &obj);
+};
diff --git a/logic/VersionFile.cpp b/logic/minecraft/VersionFile.cpp
index cd2a4f9c..93f57116 100644
--- a/logic/VersionFile.cpp
+++ b/logic/minecraft/VersionFile.cpp
@@ -1,84 +1,41 @@
#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"
+#include "logic/minecraft/VersionFile.h"
+#include "logic/minecraft/OneSixLibrary.h"
+#include "logic/minecraft/InstanceVersion.h"
+#include "logic/minecraft/JarMod.h"
+#include "ParseUtils.h"
+
+#include "logic/MMCJson.h"
using namespace MMCJson;
+#include "VersionBuildError.h"
+
#define CURRENT_MINIMUM_LAUNCHER_VERSION 14
-RawLibraryPtr RawLibrary::fromJson(const QJsonObject &libObj, const QString &filename)
+int findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
{
- 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"))
+ int retval = -1;
+ for (int i = 0; i < haystack.size(); ++i)
{
- out->applyNatives = true;
- QJsonObject nativesObj = ensureObject(libObj.value("natives"));
- for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
+ QString chunk = haystack.at(i)->rawName();
+ if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1)
{
- 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()));
- }
+ // only one is allowed.
+ if (retval != -1)
+ return -1;
+ retval = i;
}
}
- if (libObj.contains("rules"))
- {
- out->applyRules = true;
- out->rules = rulesFromJsonV4(libObj);
- }
- return out;
+ return retval;
}
VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &filename,
- const bool requireOrder, const bool isFTB)
+ const bool requireOrder, const bool isFTB)
{
VersionFilePtr out(new VersionFile());
if (doc.isEmpty() || doc.isNull())
@@ -87,7 +44,6 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
if (!doc.isObject())
{
- throw JSONValidationError("The root of " + filename + " is not an object");
}
QJsonObject root = doc.object();
@@ -111,7 +67,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
out->mcVersion = root.value("mcVersion").toString();
out->filename = filename;
- auto readString = [root, filename](const QString & key, QString & variable)
+ auto readString = [root](const QString & key, QString & variable)
{
if (root.contains(key))
{
@@ -119,6 +75,16 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
};
+ auto readStringRet = [root](const QString & key)->QString
+ {
+ if (root.contains(key))
+ {
+ return ensureString(root.value(key));
+ }
+ return QString();
+ }
+ ;
+
// FIXME: This should be ignored when applying.
if (!isFTB)
{
@@ -126,13 +92,16 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
readString("mainClass", out->mainClass);
+ readString("appletClass", out->appletClass);
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);
+
+ parse_timestamp(readStringRet("releaseTime"), out->m_releaseTimeString, out->m_releaseTime);
+ parse_timestamp(readStringRet("time"), out->m_updateTimeString, out->m_updateTime);
+
readString("assets", out->assets);
if (root.contains("minimumLauncherVersion"))
@@ -165,6 +134,14 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
}
+ if (root.contains("+traits"))
+ {
+ for (auto tweakerVal : ensureArray(root.value("+traits")))
+ {
+ out->traits.insert(ensureString(tweakerVal));
+ }
+ }
+
if (root.contains("libraries"))
{
// FIXME: This should be done when applying.
@@ -177,7 +154,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
// FIXME: This should be done when applying.
if (isFTB)
{
- lib->hint = "local";
+ lib->m_hint = "local";
lib->insertType = RawLibrary::Prepend;
out->addLibs.prepend(lib);
}
@@ -188,76 +165,29 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
}
}
+ if (root.contains("+jarMods"))
+ {
+ for (auto libVal : ensureArray(root.value("+jarMods")))
+ {
+ QJsonObject libObj = ensureObject(libVal);
+ // parse the jarmod
+ auto lib = Jarmod::fromJson(libObj, filename);
+ // and add to jar mods
+ out->jarMods.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");
- }
- }
+ auto lib = RawLibrary::fromJsonPlus(libObj, filename);
out->addLibs.append(lib);
}
}
+
if (root.contains("-libraries"))
{
for (auto libVal : ensureArray(root.value("-libraries")))
@@ -269,53 +199,81 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
return out;
}
-OneSixLibraryPtr VersionFile::createLibrary(RawLibraryPtr lib)
+QJsonDocument VersionFile::toJson(bool saveOrder)
{
- std::shared_ptr<OneSixLibrary> out(new OneSixLibrary(lib->name));
- if (!lib->url.isEmpty())
+ QJsonObject root;
+ if (saveOrder)
+ {
+ root.insert("order", order);
+ }
+ writeString(root, "name", name);
+ writeString(root, "fileId", fileId);
+ writeString(root, "version", version);
+ writeString(root, "mcVersion", mcVersion);
+ writeString(root, "id", id);
+ writeString(root, "mainClass", mainClass);
+ writeString(root, "appletClass", appletClass);
+ writeString(root, "processArguments", processArguments);
+ writeString(root, "minecraftArguments", overwriteMinecraftArguments);
+ writeString(root, "+minecraftArguments", addMinecraftArguments);
+ writeString(root, "-minecraftArguments", removeMinecraftArguments);
+ writeString(root, "type", type);
+ writeString(root, "assets", assets);
+ if (isMinecraftVersion())
+ {
+ writeString(root, "releaseTime", m_releaseTimeString);
+ writeString(root, "time", m_updateTimeString);
+ }
+ if (minimumLauncherVersion != -1)
{
- out->setBaseUrl(lib->url);
+ root.insert("minimumLauncherVersion", minimumLauncherVersion);
}
- out->setHint(lib->hint);
- if (!lib->absoluteUrl.isEmpty())
+ writeStringList(root, "tweakers", overwriteTweakers);
+ writeStringList(root, "+tweakers", addTweakers);
+ writeStringList(root, "-tweakers", removeTweakers);
+ writeStringList(root, "+traits", traits.toList());
+ writeObjectList(root, "libraries", overwriteLibs);
+ writeObjectList(root, "+libraries", addLibs);
+ writeObjectList(root, "+jarMods", jarMods);
+ // FIXME: removed libs are special snowflakes.
+ if (removeLibs.size())
{
- out->setAbsoluteUrl(lib->absoluteUrl);
+ QJsonArray array;
+ for (auto lib : removeLibs)
+ {
+ QJsonObject rmlibobj;
+ rmlibobj.insert("name", lib);
+ array.append(rmlibobj);
+ }
+ root.insert("-libraries", array);
}
- out->setAbsoluteUrl(lib->absoluteUrl);
- out->extract_excludes = lib->excludes;
- for (auto native : lib->natives)
+ // write the contents to a json document.
{
- out->addNative(native.first, native.second);
+ QJsonDocument out;
+ out.setObject(root);
+ return out;
}
- out->setRules(lib->rules);
- out->finalize();
- return out;
}
-int VersionFile::findLibrary(QList<OneSixLibraryPtr> haystack, const QString &needle)
+bool VersionFile::isMinecraftVersion()
{
- int retval = -1;
- for (int i = 0; i < haystack.size(); ++i)
- {
- QString chunk = haystack.at(i)->rawName();
- if (QRegExp(needle, Qt::CaseSensitive, QRegExp::WildcardUnix).indexIn(chunk) != -1)
- {
- // only one is allowed.
- if(retval != -1)
- return -1;
- retval = i;
- }
- }
- return retval;
+ return (fileId == "org.multimc.version.json") || (fileId == "net.minecraft") ||
+ (fileId == "org.multimc.custom.json");
+}
+
+bool VersionFile::hasJarMods()
+{
+ return !jarMods.isEmpty();
}
-void VersionFile::applyTo(VersionFinal *version)
+void VersionFile::applyTo(InstanceVersion *version)
{
if (minimumLauncherVersion != -1)
{
if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{
- throw LauncherVersionError(minimumLauncherVersion, CURRENT_MINIMUM_LAUNCHER_VERSION);
+ throw LauncherVersionError(minimumLauncherVersion,
+ CURRENT_MINIMUM_LAUNCHER_VERSION);
}
}
@@ -336,21 +294,34 @@ void VersionFile::applyTo(VersionFinal *version)
{
version->mainClass = mainClass;
}
- if (!processArguments.isNull())
+ if (!appletClass.isNull())
{
- version->processArguments = processArguments;
+ version->appletClass = appletClass;
}
- if (!type.isNull())
- {
- version->type = type;
- }
- if (!releaseTime.isNull())
+ if (!processArguments.isNull())
{
- version->releaseTime = releaseTime;
+ if (isMinecraftVersion())
+ {
+ version->vanillaProcessArguments = processArguments;
+ }
+ version->processArguments = processArguments;
}
- if (!time.isNull())
+ if (isMinecraftVersion())
{
- version->time = time;
+ if (!type.isNull())
+ {
+ version->type = type;
+ }
+ if (!m_releaseTimeString.isNull())
+ {
+ version->m_releaseTimeString = m_releaseTimeString;
+ version->m_releaseTime = m_releaseTime;
+ }
+ if (!m_updateTimeString.isNull())
+ {
+ version->m_updateTimeString = m_updateTimeString;
+ version->m_updateTime = m_updateTime;
+ }
}
if (!assets.isNull())
{
@@ -358,10 +329,15 @@ void VersionFile::applyTo(VersionFinal *version)
}
if (minimumLauncherVersion >= 0)
{
- version->minimumLauncherVersion = minimumLauncherVersion;
+ if (version->minimumLauncherVersion < minimumLauncherVersion)
+ version->minimumLauncherVersion = minimumLauncherVersion;
}
if (!overwriteMinecraftArguments.isNull())
{
+ if (isMinecraftVersion())
+ {
+ version->vanillaMinecraftArguments = overwriteMinecraftArguments;
+ }
version->minecraftArguments = overwriteMinecraftArguments;
}
if (!addMinecraftArguments.isNull())
@@ -384,13 +360,20 @@ void VersionFile::applyTo(VersionFinal *version)
{
version->tweakers.removeAll(tweaker);
}
+ version->jarMods.append(jarMods);
+ version->traits.unite(traits);
if (shouldOverwriteLibs)
{
- version->libraries.clear();
+ QList<OneSixLibraryPtr> libs;
for (auto lib : overwriteLibs)
{
- version->libraries.append(createLibrary(lib));
+ libs.append(OneSixLibrary::fromRawLibrary(lib));
+ }
+ if (isMinecraftVersion())
+ {
+ version->vanillaLibraries = libs;
}
+ version->libraries = libs;
}
for (auto lib : addLibs)
{
@@ -399,43 +382,46 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Apply:
{
// QLOG_INFO() << "Applying lib " << lib->name;
- int index = findLibrary(version->libraries, lib->name);
+ int index = findLibrary(version->libraries, lib->m_name);
if (index >= 0)
{
auto library = version->libraries[index];
- if (!lib->url.isNull())
+ if (!lib->m_base_url.isNull())
{
- library->setBaseUrl(lib->url);
+ library->setBaseUrl(lib->m_base_url);
}
- if (!lib->hint.isNull())
+ if (!lib->m_hint.isNull())
{
- library->setHint(lib->hint);
+ library->setHint(lib->m_hint);
}
- if (!lib->absoluteUrl.isNull())
+ if (!lib->m_absolute_url.isNull())
{
- library->setAbsoluteUrl(lib->absoluteUrl);
+ library->setAbsoluteUrl(lib->m_absolute_url);
}
if (lib->applyExcludes)
{
- library->extract_excludes = lib->excludes;
+ library->extract_excludes = lib->extract_excludes;
}
- if (lib->applyNatives)
+ if (lib->isNative())
{
- library->clearSuffixes();
+ // library->clearSuffixes();
+ library->m_native_suffixes = lib->m_native_suffixes;
+ /*
for (auto native : lib->natives)
{
library->addNative(native.first, native.second);
}
+ */
}
if (lib->applyRules)
{
- library->setRules(lib->rules);
+ library->setRules(lib->m_rules);
}
library->finalize();
}
else
{
- QLOG_WARN() << "Couldn't find" << lib->name << "(skipping)";
+ QLOG_WARN() << "Couldn't find" << lib->m_name << "(skipping)";
}
break;
}
@@ -443,24 +429,24 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Prepend:
{
// QLOG_INFO() << "Adding lib " << lib->name;
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
const int index = findLibrary(
- version->libraries, QString(lib->name).replace(startOfVersion, INT_MAX, '*'));
+ version->libraries, QString(lib->m_name).replace(startOfVersion, INT_MAX, '*'));
if (index < 0)
{
if (lib->insertType == RawLibrary::Append)
{
- version->libraries.append(createLibrary(lib));
+ version->libraries.append(OneSixLibrary::fromRawLibrary(lib));
}
else
{
- version->libraries.prepend(createLibrary(lib));
+ version->libraries.prepend(OneSixLibrary::fromRawLibrary(lib));
}
}
else
{
auto otherLib = version->libraries.at(index);
- const Util::Version ourVersion = lib->name.mid(startOfVersion, INT_MAX);
+ const Util::Version ourVersion = lib->m_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
@@ -474,7 +460,7 @@ void VersionFile::applyTo(VersionFinal *version)
throw VersionBuildError(
QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name, filename));
+ .arg(otherLib->rawName(), lib->m_name, filename));
}
else
{
@@ -486,7 +472,7 @@ void VersionFile::applyTo(VersionFinal *version)
// if we are higher it means we should update
if (ourVersion > otherVersion)
{
- auto library = createLibrary(lib);
+ auto library = OneSixLibrary::fromRawLibrary(lib);
if (Util::Version(otherLib->minVersion) < ourVersion)
{
library->minVersion = ourVersion.toString();
@@ -501,7 +487,7 @@ void VersionFile::applyTo(VersionFinal *version)
{
throw VersionBuildError(QObject::tr(
"Error resolving library dependencies between %1 and %2 in %3.")
- .arg(otherLib->rawName(), lib->name,
+ .arg(otherLib->rawName(), lib->m_name,
filename));
}
}
@@ -512,10 +498,10 @@ void VersionFile::applyTo(VersionFinal *version)
case RawLibrary::Replace:
{
QString toReplace;
- if(lib->insertData.isEmpty())
+ if (lib->insertData.isEmpty())
{
- const int startOfVersion = lib->name.lastIndexOf(':') + 1;
- toReplace = QString(lib->name).replace(startOfVersion, INT_MAX, '*');
+ const int startOfVersion = lib->m_name.lastIndexOf(':') + 1;
+ toReplace = QString(lib->m_name).replace(startOfVersion, INT_MAX, '*');
}
else
toReplace = lib->insertData;
@@ -523,7 +509,7 @@ void VersionFile::applyTo(VersionFinal *version)
int index = findLibrary(version->libraries, toReplace);
if (index >= 0)
{
- version->libraries.replace(index, createLibrary(lib));
+ version->libraries.replace(index, OneSixLibrary::fromRawLibrary(lib));
}
else
{
diff --git a/logic/minecraft/VersionFile.h b/logic/minecraft/VersionFile.h
new file mode 100644
index 00000000..186f4335
--- /dev/null
+++ b/logic/minecraft/VersionFile.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <QString>
+#include <QStringList>
+#include <QDateTime>
+#include <memory>
+#include "logic/minecraft/OpSys.h"
+#include "logic/minecraft/OneSixRule.h"
+#include "VersionPatch.h"
+#include "MMCError.h"
+#include "OneSixLibrary.h"
+#include "JarMod.h"
+
+class InstanceVersion;
+struct VersionFile;
+
+typedef std::shared_ptr<VersionFile> VersionFilePtr;
+class VersionFile : public VersionPatch
+{
+public: /* methods */
+ static VersionFilePtr fromJson(const QJsonDocument &doc, const QString &filename,
+ const bool requireOrder, const bool isFTB = false);
+ QJsonDocument toJson(bool saveOrder);
+
+ virtual void applyTo(InstanceVersion *version) override;
+ virtual bool isMinecraftVersion() override;
+ virtual bool hasJarMods() override;
+ virtual int getOrder() override
+ {
+ return order;
+ }
+ virtual void setOrder(int order) override
+ {
+ this->order = order;
+ }
+ virtual QList<JarmodPtr> getJarMods() override
+ {
+ return jarMods;
+ }
+ virtual QString getPatchID() override
+ {
+ return fileId;
+ }
+ virtual QString getPatchName() override
+ {
+ return name;
+ }
+ virtual QString getPatchVersion() override
+ {
+ return version;
+ }
+ virtual QString getPatchFilename() override
+ {
+ return filename;
+ }
+
+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 appletClass;
+ QString overwriteMinecraftArguments;
+ QString addMinecraftArguments;
+ QString removeMinecraftArguments;
+ QString processArguments;
+ QString type;
+
+ /// the time this version was actually released by Mojang, as string and as QDateTime
+ QString m_releaseTimeString;
+ QDateTime m_releaseTime;
+
+ /// the time this version was last updated by Mojang, as string and as QDateTime
+ QString m_updateTimeString;
+ QDateTime m_updateTime;
+
+ /// asset group used by this ... thing.
+ 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;
+
+ QSet<QString> traits;
+
+ QList<JarmodPtr> jarMods;
+};
+
+
diff --git a/logic/minecraft/VersionPatch.h b/logic/minecraft/VersionPatch.h
new file mode 100644
index 00000000..1dd30e79
--- /dev/null
+++ b/logic/minecraft/VersionPatch.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <memory>
+#include <QList>
+#include "JarMod.h"
+
+class InstanceVersion;
+class VersionPatch
+{
+public:
+ virtual ~VersionPatch(){};
+ virtual void applyTo(InstanceVersion *version) = 0;
+
+ virtual bool isMinecraftVersion() = 0;
+ virtual bool hasJarMods() = 0;
+ virtual QList<JarmodPtr> getJarMods() = 0;
+
+ virtual bool isMoveable()
+ {
+ return getOrder() >= 0;
+ }
+ virtual void setOrder(int order) = 0;
+ virtual int getOrder() = 0;
+
+ virtual QString getPatchID() = 0;
+ virtual QString getPatchName() = 0;
+ virtual QString getPatchVersion() = 0;
+ virtual QString getPatchFilename() = 0;
+};
+
+typedef std::shared_ptr<VersionPatch> VersionPatchPtr;
diff --git a/logic/minecraft/VersionSource.h b/logic/minecraft/VersionSource.h
new file mode 100644
index 00000000..75b2c24b
--- /dev/null
+++ b/logic/minecraft/VersionSource.h
@@ -0,0 +1,9 @@
+#pragma once
+
+/// where is a version from?
+enum VersionSource
+{
+ Builtin, //!< version loaded from the internal resources.
+ Local, //!< version loaded from a file in the cache.
+ Remote, //!< incomplete version on a remote server.
+}; \ No newline at end of file
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
index 2df8428b..d05e7b6f 100644
--- a/logic/net/NetJob.h
+++ b/logic/net/NetJob.h
@@ -21,7 +21,6 @@
#include "MD5EtagDownload.h"
#include "CacheDownload.h"
#include "HttpMetaCache.h"
-#include "ForgeXzDownload.h"
#include "logic/tasks/ProgressProvider.h"
class NetJob;
diff --git a/logic/screenshots/ImgurAlbumCreation.cpp b/logic/screenshots/ImgurAlbumCreation.cpp
index e473952e..91cf4bb1 100644
--- a/logic/screenshots/ImgurAlbumCreation.cpp
+++ b/logic/screenshots/ImgurAlbumCreation.cpp
@@ -4,8 +4,8 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrl>
+#include <QStringList>
-#include "logic/screenshots//ScreenshotList.h"
#include "logic/net/URLConstants.h"
#include "MultiMC.h"
#include "logger/QsLog.h"
@@ -28,7 +28,7 @@ void ImgurAlbumCreation::start()
QStringList ids;
for (auto shot : m_screenshots)
{
- ids.append(shot->imgurId);
+ ids.append(shot->m_imgurId);
}
const QByteArray data = "ids=" + ids.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
diff --git a/logic/screenshots/ImgurUpload.cpp b/logic/screenshots/ImgurUpload.cpp
index 62033ef5..f305aec0 100644
--- a/logic/screenshots/ImgurUpload.cpp
+++ b/logic/screenshots/ImgurUpload.cpp
@@ -8,7 +8,6 @@
#include <QFile>
#include <QUrl>
-#include "logic/screenshots/ScreenshotList.h"
#include "logic/net/URLConstants.h"
#include "MultiMC.h"
#include "logger/QsLog.h"
@@ -27,7 +26,7 @@ void ImgurUpload::start()
request.setRawHeader("Authorization", "Client-ID 5b97b0713fba4a3");
request.setRawHeader("Accept", "application/json");
- QFile f(m_shot->file);
+ QFile f(m_shot->m_file.absoluteFilePath());
if (!f.open(QFile::ReadOnly))
{
emit failed(m_index_within_job);
@@ -46,7 +45,7 @@ void ImgurUpload::start()
multipart->append(typePart);
QHttpPart namePart;
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
- namePart.setBody(m_shot->timestamp.toString(Qt::ISODate).toUtf8());
+ namePart.setBody(m_shot->m_file.baseName().toUtf8());
multipart->append(namePart);
auto worker = MMC->qnam();
@@ -84,8 +83,8 @@ void ImgurUpload::downloadFinished()
emit failed(m_index_within_job);
return;
}
- m_shot->imgurId = object.value("data").toObject().value("id").toString();
- m_shot->url = object.value("data").toObject().value("link").toString();
+ m_shot->m_imgurId = object.value("data").toObject().value("id").toString();
+ m_shot->m_url = object.value("data").toObject().value("link").toString();
m_status = Job_Finished;
emit succeeded(m_index_within_job);
return;
diff --git a/logic/screenshots/Screenshot.cpp b/logic/screenshots/Screenshot.cpp
deleted file mode 100644
index 882e491f..00000000
--- a/logic/screenshots/Screenshot.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "Screenshot.h"
-#include <QImage>
-#include <QIcon>
-QIcon ScreenShot::getImage()
-{
- if(!imageloaded)
- {
- QImage image(file);
- QImage thumbnail = image.scaledToWidth(256, Qt::SmoothTransformation);
- m_image = QIcon(QPixmap::fromImage(thumbnail));
- imageloaded = true;
- }
- return m_image;
-}
diff --git a/logic/screenshots/Screenshot.h b/logic/screenshots/Screenshot.h
index 815c0d47..b48cbe99 100644
--- a/logic/screenshots/Screenshot.h
+++ b/logic/screenshots/Screenshot.h
@@ -2,18 +2,18 @@
#include <QDateTime>
#include <QString>
+#include <QFileInfo>
#include <memory>
-#include <QIcon>
struct ScreenShot
{
- QIcon getImage();
- QIcon m_image;
- bool imageloaded = false;
- QDateTime timestamp;
- QString file;
- QString url;
- QString imgurId;
+ ScreenShot(QFileInfo file)
+ {
+ m_file = file;
+ }
+ QFileInfo m_file;
+ QString m_url;
+ QString m_imgurId;
};
typedef std::shared_ptr<ScreenShot> ScreenshotPtr;
diff --git a/logic/screenshots/ScreenshotList.cpp b/logic/screenshots/ScreenshotList.cpp
deleted file mode 100644
index a34f4d46..00000000
--- a/logic/screenshots/ScreenshotList.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "ScreenshotList.h"
-#include "gui/dialogs/ScreenshotDialog.h"
-
-#include <QDir>
-#include <QIcon>
-#include <QList>
-#include "gui/dialogs/ProgressDialog.h"
-#include "gui/dialogs/CustomMessageBox.h"
-
-ScreenshotList::ScreenshotList(InstancePtr instance, QObject *parent)
- : QAbstractListModel(parent), m_instance(instance)
-{
-}
-
-int ScreenshotList::rowCount(const QModelIndex &) const
-{
- return m_screenshots.size();
-}
-
-QVariant ScreenshotList::data(const QModelIndex &index, int role) const
-{
- if (index.row() >= m_screenshots.size() || index.row() < 0)
- return QVariant();
-
- switch (role)
- {
- case Qt::DecorationRole:
- return m_screenshots.at(index.row())->getImage();
- case Qt::DisplayRole:
- return m_screenshots.at(index.row())->timestamp.toString("yyyy-MM-dd HH:mm:ss");
- case Qt::ToolTipRole:
- return m_screenshots.at(index.row())->timestamp.toString("yyyy-MM-dd HH:mm:ss");
- case Qt::TextAlignmentRole:
- return (int)(Qt::AlignHCenter | Qt::AlignVCenter);
- default:
- return QVariant();
- }
-}
-
-QVariant ScreenshotList::headerData(int section, Qt::Orientation orientation, int role) const
-{
- return QVariant();
-}
-
-Qt::ItemFlags ScreenshotList::flags(const QModelIndex &index) const
-{
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-}
-
-Task *ScreenshotList::load()
-{
- return new ScreenshotLoadTask(this);
-}
-
-ScreenshotLoadTask::ScreenshotLoadTask(ScreenshotList *list) : m_list(list)
-{
-}
-
-ScreenshotLoadTask::~ScreenshotLoadTask()
-{
-}
-
-void ScreenshotLoadTask::executeTask()
-{
- auto dir = QDir(m_list->instance()->minecraftRoot());
- if (!dir.cd("screenshots"))
- {
- emitFailed("Selected instance does not have any screenshots!");
- return;
- }
- dir.setNameFilters(QStringList() << "*.png");
- this->m_results.clear();
- for (auto file : dir.entryList())
- {
- ScreenShot *shot = new ScreenShot();
- shot->timestamp = QDateTime::fromString(file, "yyyy-MM-dd_HH.mm.ss.png");
- shot->file = dir.absoluteFilePath(file);
- m_results.append(ScreenshotPtr(shot));
- }
- m_list->loadShots(m_results);
- emitSucceeded();
-}
-
-void ScreenshotList::deleteSelected(ScreenshotDialog *dialog)
-{
- auto screens = dialog->selected();
- if (screens.isEmpty())
- {
- return;
- }
- beginResetModel();
- QList<std::shared_ptr<ScreenShot>>::const_iterator it;
- for (it = screens.cbegin(); it != screens.cend(); it++)
- {
- auto shot = *it;
- if (!QFile(shot->file).remove())
- {
- CustomMessageBox::selectable(dialog, tr("Error!"),
- tr("Failed to delete screenshots!"),
- QMessageBox::Warning)->exec();
- break;
- }
- }
- ProgressDialog refresh(dialog);
- Task *t = load();
- if (refresh.exec(t) != QDialog::Accepted)
- {
- CustomMessageBox::selectable(dialog, tr("Error!"),
- tr("Unable to refresh list: %1").arg(t->failReason()),
- QMessageBox::Warning)->exec();
- }
- endResetModel();
-}
diff --git a/logic/screenshots/ScreenshotList.h b/logic/screenshots/ScreenshotList.h
deleted file mode 100644
index dc26a698..00000000
--- a/logic/screenshots/ScreenshotList.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#pragma once
-
-#include <QAbstractListModel>
-#include "logic/BaseInstance.h"
-#include "logic/tasks/Task.h"
-
-#include "Screenshot.h"
-
-class ScreenshotList : public QAbstractListModel
-{
- Q_OBJECT
-public:
- ScreenshotList(InstancePtr instance, QObject *parent = 0);
-
- QVariant data(const QModelIndex &index, int role) const;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-
- int rowCount(const QModelIndex &parent) const;
-
- Qt::ItemFlags flags(const QModelIndex &index) const;
-
- Task *load();
-
- void loadShots(QList<ScreenshotPtr> shots)
- {
- m_screenshots = shots;
- }
-
- QList<ScreenshotPtr> screenshots() const
- {
- return m_screenshots;
- }
-
- InstancePtr instance() const
- {
- return m_instance;
- }
-
- void deleteSelected(class ScreenshotDialog *dialog);
-
-signals:
-
-public
-slots:
-
-private:
- QList<ScreenshotPtr> m_screenshots;
- InstancePtr m_instance;
-};
-
-class ScreenshotLoadTask : public Task
-{
- Q_OBJECT
-
-public:
- explicit ScreenshotLoadTask(ScreenshotList *list);
- ~ScreenshotLoadTask();
-
- QList<ScreenshotPtr> screenShots() const
- {
- return m_results;
- }
-
-protected:
- virtual void executeTask();
-
-private:
- ScreenshotList *m_list;
- QList<ScreenshotPtr> m_results;
-};
diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h
index 15e453a3..dcb71139 100644
--- a/logic/tasks/ProgressProvider.h
+++ b/logic/tasks/ProgressProvider.h
@@ -32,6 +32,7 @@ signals:
void status(QString status);
public:
+ virtual ~ProgressProvider() {};
virtual QString getStatus() const = 0;
virtual void getProgress(qint64 &current, qint64 &total) = 0;
virtual bool isRunning() const = 0;