summaryrefslogtreecommitdiffstats
path: root/api/logic/minecraft/ftb
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic/minecraft/ftb')
-rw-r--r--api/logic/minecraft/ftb/FTBPlugin.cpp395
-rw-r--r--api/logic/minecraft/ftb/FTBPlugin.h13
-rw-r--r--api/logic/minecraft/ftb/FTBProfileStrategy.cpp128
-rw-r--r--api/logic/minecraft/ftb/FTBProfileStrategy.h21
-rw-r--r--api/logic/minecraft/ftb/FTBVersion.h32
-rw-r--r--api/logic/minecraft/ftb/LegacyFTBInstance.cpp27
-rw-r--r--api/logic/minecraft/ftb/LegacyFTBInstance.h17
-rw-r--r--api/logic/minecraft/ftb/OneSixFTBInstance.cpp138
-rw-r--r--api/logic/minecraft/ftb/OneSixFTBInstance.h30
9 files changed, 801 insertions, 0 deletions
diff --git a/api/logic/minecraft/ftb/FTBPlugin.cpp b/api/logic/minecraft/ftb/FTBPlugin.cpp
new file mode 100644
index 00000000..a142c106
--- /dev/null
+++ b/api/logic/minecraft/ftb/FTBPlugin.cpp
@@ -0,0 +1,395 @@
+#include "FTBPlugin.h"
+#include <Env.h>
+#include "FTBVersion.h"
+#include "LegacyFTBInstance.h"
+#include "OneSixFTBInstance.h"
+#include <BaseInstance.h>
+#include <InstanceList.h>
+#include <minecraft/MinecraftVersionList.h>
+#include <settings/INISettingsObject.h>
+#include <FileSystem.h>
+#include "QDebug"
+#include <QXmlStreamReader>
+#include <QRegularExpression>
+
+struct FTBRecord
+{
+ QString dirName;
+ QString name;
+ QString logo;
+ QString iconKey;
+ QString mcVersion;
+ QString description;
+ QString instanceDir;
+ QString templateDir;
+ bool operator==(const FTBRecord other) const
+ {
+ return instanceDir == other.instanceDir;
+ }
+};
+
+inline uint qHash(FTBRecord record)
+{
+ return qHash(record.instanceDir);
+}
+
+QSet<FTBRecord> discoverFTBInstances(SettingsObjectPtr globalSettings)
+{
+ QSet<FTBRecord> records;
+ QDir dir = QDir(globalSettings->get("FTBLauncherLocal").toString());
+ QDir dataDir = QDir(globalSettings->get("FTBRoot").toString());
+ if (!dataDir.exists())
+ {
+ qDebug() << "The FTB directory specified does not exist. Please check your settings";
+ return records;
+ }
+ else if (!dir.exists())
+ {
+ qDebug() << "The FTB launcher data directory specified does not exist. Please check "
+ "your settings";
+ return records;
+ }
+ dir.cd("ModPacks");
+ auto allFiles = dir.entryList(QDir::Readable | QDir::Files, QDir::Name);
+ for (auto filename : allFiles)
+ {
+ if (!filename.endsWith(".xml"))
+ continue;
+ auto fpath = dir.absoluteFilePath(filename);
+ QFile f(fpath);
+ qDebug() << "Discovering FTB instances -- " << fpath;
+ if (!f.open(QFile::ReadOnly))
+ continue;
+
+ // read the FTB packs XML.
+ QXmlStreamReader reader(&f);
+ while (!reader.atEnd())
+ {
+ switch (reader.readNext())
+ {
+ case QXmlStreamReader::StartElement:
+ {
+ if (reader.name() == "modpack")
+ {
+ QXmlStreamAttributes attrs = reader.attributes();
+ FTBRecord record;
+ record.dirName = attrs.value("dir").toString();
+ record.instanceDir = dataDir.absoluteFilePath(record.dirName);
+ record.templateDir = dir.absoluteFilePath(record.dirName);
+ QDir test(record.instanceDir);
+ qDebug() << dataDir.absolutePath() << record.instanceDir << record.dirName;
+ if (!test.exists())
+ continue;
+ record.name = attrs.value("name").toString();
+ record.logo = attrs.value("logo").toString();
+ QString logo = record.logo;
+ record.iconKey = logo.remove(QRegularExpression("\\..*"));
+ auto customVersions = attrs.value("customMCVersions");
+ if (!customVersions.isNull())
+ {
+ QMap<QString, QString> versionMatcher;
+ QString customVersionsStr = customVersions.toString();
+ QStringList list = customVersionsStr.split(';');
+ for (auto item : list)
+ {
+ auto segment = item.split('^');
+ if (segment.size() != 2)
+ {
+ qCritical() << "FTB: Segment of size < 2 in "
+ << customVersionsStr;
+ continue;
+ }
+ versionMatcher[segment[0]] = segment[1];
+ }
+ auto actualVersion = attrs.value("version").toString();
+ if (versionMatcher.contains(actualVersion))
+ {
+ record.mcVersion = versionMatcher[actualVersion];
+ }
+ else
+ {
+ record.mcVersion = attrs.value("mcVersion").toString();
+ }
+ }
+ else
+ {
+ record.mcVersion = attrs.value("mcVersion").toString();
+ }
+ record.description = attrs.value("description").toString();
+ records.insert(record);
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ break;
+ case QXmlStreamReader::Characters:
+ break;
+ default:
+ break;
+ }
+ }
+ f.close();
+ }
+ return records;
+}
+
+InstancePtr loadInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record)
+{
+ InstancePtr inst;
+
+ auto m_settings = std::make_shared<INISettingsObject>(FS::PathCombine(record.instanceDir, "instance.cfg"));
+ m_settings->registerSetting("InstanceType", "Legacy");
+
+ qDebug() << "Loading existing " << record.name;
+
+ QString inst_type = m_settings->get("InstanceType").toString();
+ if (inst_type == "LegacyFTB")
+ {
+ inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir));
+ }
+ else if (inst_type == "OneSixFTB")
+ {
+ inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir));
+ }
+ else
+ {
+ return nullptr;
+ }
+ qDebug() << "Construction " << record.instanceDir;
+
+ SettingsObject::Lock lock(inst->settings());
+ inst->init();
+ qDebug() << "Init " << record.instanceDir;
+ inst->setGroupInitial("FTB");
+ /**
+ * FIXME: this does not respect the user's preferences. BUT, it would work nicely with the planned pack support
+ * -> instead of changing the user values, change pack values (defaults you can look at and revert to)
+ */
+ /*
+ inst->setName(record.name);
+ inst->setIconKey(record.iconKey);
+ inst->setNotes(record.description);
+ */
+ if (inst->intendedVersionId() != record.mcVersion)
+ {
+ inst->setIntendedVersionId(record.mcVersion);
+ }
+ qDebug() << "Post-Process " << record.instanceDir;
+ if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap))
+ {
+ return nullptr;
+ }
+ qDebug() << "Final " << record.instanceDir;
+ return inst;
+}
+
+InstancePtr createInstance(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, const FTBRecord & record)
+{
+ QDir rootDir(record.instanceDir);
+
+ InstancePtr inst;
+
+ qDebug() << "Converting " << record.name << " as new.";
+
+ auto mcVersion = std::dynamic_pointer_cast<MinecraftVersion>(ENV.getVersion("net.minecraft", record.mcVersion));
+ if (!mcVersion)
+ {
+ qCritical() << "Can't load instance " << record.instanceDir
+ << " because minecraft version " << record.mcVersion
+ << " can't be resolved.";
+ return nullptr;
+ }
+
+ if (!rootDir.exists() && !rootDir.mkpath("."))
+ {
+ qCritical() << "Can't create instance folder" << record.instanceDir;
+ return nullptr;
+ }
+
+ auto m_settings = std::make_shared<INISettingsObject>(FS::PathCombine(record.instanceDir, "instance.cfg"));
+ m_settings->registerSetting("InstanceType", "Legacy");
+
+ if (mcVersion->usesLegacyLauncher())
+ {
+ m_settings->set("InstanceType", "LegacyFTB");
+ inst.reset(new LegacyFTBInstance(globalSettings, m_settings, record.instanceDir));
+ }
+ else
+ {
+ m_settings->set("InstanceType", "OneSixFTB");
+ inst.reset(new OneSixFTBInstance(globalSettings, m_settings, record.instanceDir));
+ }
+ // initialize
+ {
+ SettingsObject::Lock lock(inst->settings());
+ inst->setIntendedVersionId(mcVersion->descriptor());
+ inst->init();
+ inst->setGroupInitial("FTB");
+ inst->setName(record.name);
+ inst->setIconKey(record.iconKey);
+ inst->setNotes(record.description);
+ qDebug() << "Post-Process " << record.instanceDir;
+ if (!InstanceList::continueProcessInstance(inst, InstanceList::NoCreateError, record.instanceDir, groupMap))
+ {
+ return nullptr;
+ }
+ }
+ return inst;
+}
+
+void FTBPlugin::loadInstances(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, QList<InstancePtr> &tempList)
+{
+ // nothing to load when we don't have
+ if (globalSettings->get("TrackFTBInstances").toBool() != true)
+ {
+ return;
+ }
+
+ auto records = discoverFTBInstances(globalSettings);
+ if (!records.size())
+ {
+ qDebug() << "No FTB instances to load.";
+ return;
+ }
+ qDebug() << "Loading FTB instances! -- got " << records.size();
+ // process the records we acquired.
+ for (auto record : records)
+ {
+ qDebug() << "Loading FTB instance from " << record.instanceDir;
+ QString iconKey = record.iconKey;
+ // MMC->icons()->addIcon(iconKey, iconKey, FS::PathCombine(record.templateDir, record.logo), MMCIcon::Transient);
+ auto settingsFilePath = FS::PathCombine(record.instanceDir, "instance.cfg");
+ qDebug() << "ICON get!";
+
+ if (QFileInfo(settingsFilePath).exists())
+ {
+ auto instPtr = loadInstance(globalSettings, groupMap, record);
+ if (!instPtr)
+ {
+ qWarning() << "Couldn't load instance config:" << settingsFilePath;
+ if(!QFile::remove(settingsFilePath))
+ {
+ qWarning() << "Couldn't remove broken instance config!";
+ continue;
+ }
+ // failed to load, but removed the poisonous file
+ }
+ else
+ {
+ tempList.append(InstancePtr(instPtr));
+ continue;
+ }
+ }
+ auto instPtr = createInstance(globalSettings, groupMap, record);
+ if (!instPtr)
+ {
+ qWarning() << "Couldn't create FTB instance!";
+ continue;
+ }
+ tempList.append(InstancePtr(instPtr));
+ }
+}
+
+#ifdef Q_OS_WIN32
+#include <windows.h>
+static const int APPDATA_BUFFER_SIZE = 1024;
+#endif
+
+static QString getLocalCacheStorageLocation()
+{
+ QString ftbDefault;
+#ifdef Q_OS_WIN32
+ wchar_t buf[APPDATA_BUFFER_SIZE];
+ if (GetEnvironmentVariableW(L"LOCALAPPDATA", buf, APPDATA_BUFFER_SIZE)) // local
+ {
+ ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
+ }
+ else if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE)) // roaming
+ {
+ ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
+ }
+ else
+ {
+ qCritical() << "Your LOCALAPPDATA and APPDATA folders are missing!"
+ " If you are on windows, this means your system is broken.";
+ }
+#elif defined(Q_OS_MAC)
+ ftbDefault = FS::PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
+#else
+ ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
+#endif
+ return ftbDefault;
+}
+
+
+static QString getRoamingStorageLocation()
+{
+ QString ftbDefault;
+#ifdef Q_OS_WIN32
+ wchar_t buf[APPDATA_BUFFER_SIZE];
+ QString cacheStorage;
+ if (GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE))
+ {
+ ftbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
+ }
+ else
+ {
+ qCritical() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken.";
+ }
+#elif defined(Q_OS_MAC)
+ ftbDefault = FS::PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
+#else
+ ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
+#endif
+ return ftbDefault;
+}
+
+void FTBPlugin::initialize(SettingsObjectPtr globalSettings)
+{
+ // FTB
+ globalSettings->registerSetting("TrackFTBInstances", false);
+ QString ftbRoaming = getRoamingStorageLocation();
+ QString ftbLocal = getLocalCacheStorageLocation();
+
+ globalSettings->registerSetting("FTBLauncherRoaming", ftbRoaming);
+ globalSettings->registerSetting("FTBLauncherLocal", ftbLocal);
+ qDebug() << "FTB Launcher paths:" << globalSettings->get("FTBLauncherRoaming").toString()
+ << "and" << globalSettings->get("FTBLauncherLocal").toString();
+
+ globalSettings->registerSetting("FTBRoot");
+ if (globalSettings->get("FTBRoot").isNull())
+ {
+ QString ftbRoot;
+ QFile f(QDir(globalSettings->get("FTBLauncherRoaming").toString()).absoluteFilePath("ftblaunch.cfg"));
+ qDebug() << "Attempting to read" << f.fileName();
+ if (f.open(QFile::ReadOnly))
+ {
+ const QString data = QString::fromLatin1(f.readAll());
+ QRegularExpression exp("installPath=(.*)");
+ ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
+#ifdef Q_OS_WIN32
+ if (!ftbRoot.isEmpty())
+ {
+ if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
+ {
+ ftbRoot.remove(1, 1);
+ }
+ }
+#endif
+ if (ftbRoot.isEmpty())
+ {
+ qDebug() << "Failed to get FTB root path";
+ }
+ else
+ {
+ qDebug() << "FTB is installed at" << ftbRoot;
+ globalSettings->set("FTBRoot", ftbRoot);
+ }
+ }
+ else
+ {
+ qWarning() << "Couldn't open" << f.fileName() << ":" << f.errorString();
+ qWarning() << "This is perfectly normal if you don't have FTB installed";
+ }
+ }
+}
diff --git a/api/logic/minecraft/ftb/FTBPlugin.h b/api/logic/minecraft/ftb/FTBPlugin.h
new file mode 100644
index 00000000..6851d8a5
--- /dev/null
+++ b/api/logic/minecraft/ftb/FTBPlugin.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <BaseInstance.h>
+
+#include "multimc_logic_export.h"
+
+// Pseudo-plugin for FTB related things. Super derpy!
+class MULTIMC_LOGIC_EXPORT FTBPlugin
+{
+public:
+ static void initialize(SettingsObjectPtr globalSettings);
+ static void loadInstances(SettingsObjectPtr globalSettings, QMap<QString, QString> &groupMap, QList<InstancePtr> &tempList);
+};
diff --git a/api/logic/minecraft/ftb/FTBProfileStrategy.cpp b/api/logic/minecraft/ftb/FTBProfileStrategy.cpp
new file mode 100644
index 00000000..f5faacae
--- /dev/null
+++ b/api/logic/minecraft/ftb/FTBProfileStrategy.cpp
@@ -0,0 +1,128 @@
+#include "FTBProfileStrategy.h"
+#include "OneSixFTBInstance.h"
+
+#include "minecraft/VersionBuildError.h"
+#include "minecraft/MinecraftVersionList.h"
+#include <FileSystem.h>
+
+#include <QDir>
+#include <QUuid>
+#include <QJsonDocument>
+#include <QJsonArray>
+
+FTBProfileStrategy::FTBProfileStrategy(OneSixFTBInstance* instance) : OneSixProfileStrategy(instance)
+{
+}
+
+void FTBProfileStrategy::loadDefaultBuiltinPatches()
+{
+ // FIXME: this should be here, but it needs us to be able to deal with multiple libraries paths
+ // OneSixProfileStrategy::loadDefaultBuiltinPatches();
+ auto mcVersion = m_instance->intendedVersionId();
+ auto nativeInstance = dynamic_cast<OneSixFTBInstance *>(m_instance);
+
+ ProfilePatchPtr minecraftPatch;
+ {
+ auto mcJson = m_instance->versionsPath().absoluteFilePath(mcVersion + "/" + mcVersion + ".json");
+ // load up the base minecraft patch
+ if(QFile::exists(mcJson))
+ {
+ auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false);
+ file->fileId = "net.minecraft";
+ file->name = QObject::tr("Minecraft (tracked)");
+ file->setVanilla(true);
+ if(file->version.isEmpty())
+ {
+ file->version = mcVersion;
+ }
+ for(auto addLib: file->libraries)
+ {
+ addLib->setHint("local");
+ addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath());
+ }
+ minecraftPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
+ }
+ else
+ {
+ throw VersionIncomplete("net.minecraft");
+ }
+ minecraftPatch->setOrder(-2);
+ }
+ profile->appendPatch(minecraftPatch);
+
+ ProfilePatchPtr packPatch;
+ {
+ auto mcJson = m_instance->minecraftRoot() + "/pack.json";
+ // load up the base minecraft patch
+ if(QFile::exists(mcJson))
+ {
+ auto file = ProfileUtils::parseJsonFile(QFileInfo(mcJson), false);
+
+ // adapt the loaded file - the FTB patch file format is different than ours.
+ file->minecraftVersion.clear();
+ for(auto addLib: file->libraries)
+ {
+ addLib->setHint("local");
+ addLib->setStoragePrefix(nativeInstance->librariesPath().absolutePath());
+ }
+ file->fileId = "org.multimc.ftb.pack";
+ file->setVanilla(true);
+ file->name = QObject::tr("%1 (FTB pack)").arg(m_instance->name());
+ if(file->version.isEmpty())
+ {
+ file->version = QObject::tr("Unknown");
+ QFile versionFile (FS::PathCombine(m_instance->instanceRoot(), "version"));
+ if(versionFile.exists())
+ {
+ if(versionFile.open(QIODevice::ReadOnly))
+ {
+ // FIXME: just guessing the encoding/charset here.
+ auto version = QString::fromUtf8(versionFile.readAll());
+ file->version = version;
+ }
+ }
+ }
+ packPatch = std::dynamic_pointer_cast<ProfilePatch>(file);
+ }
+ else
+ {
+ throw VersionIncomplete("org.multimc.ftb.pack");
+ }
+ packPatch->setOrder(1);
+ }
+ profile->appendPatch(packPatch);
+
+}
+
+void FTBProfileStrategy::load()
+{
+ profile->clearPatches();
+
+ loadDefaultBuiltinPatches();
+ loadUserPatches();
+}
+
+bool FTBProfileStrategy::saveOrder(ProfileUtils::PatchOrder order)
+{
+ return false;
+}
+
+bool FTBProfileStrategy::resetOrder()
+{
+ return false;
+}
+
+bool FTBProfileStrategy::installJarMods(QStringList filepaths)
+{
+ return false;
+}
+
+bool FTBProfileStrategy::customizePatch(ProfilePatchPtr patch)
+{
+ return false;
+}
+
+bool FTBProfileStrategy::revertPatch(ProfilePatchPtr patch)
+{
+ return false;
+}
diff --git a/api/logic/minecraft/ftb/FTBProfileStrategy.h b/api/logic/minecraft/ftb/FTBProfileStrategy.h
new file mode 100644
index 00000000..522af098
--- /dev/null
+++ b/api/logic/minecraft/ftb/FTBProfileStrategy.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "minecraft/ProfileStrategy.h"
+#include "minecraft/onesix/OneSixProfileStrategy.h"
+
+class OneSixFTBInstance;
+
+class FTBProfileStrategy : public OneSixProfileStrategy
+{
+public:
+ FTBProfileStrategy(OneSixFTBInstance * instance);
+ virtual ~FTBProfileStrategy() {};
+ virtual void load() override;
+ virtual bool resetOrder() override;
+ virtual bool saveOrder(ProfileUtils::PatchOrder order) override;
+ virtual bool installJarMods(QStringList filepaths) override;
+ virtual bool customizePatch (ProfilePatchPtr patch) override;
+ virtual bool revertPatch (ProfilePatchPtr patch) override;
+
+protected:
+ virtual void loadDefaultBuiltinPatches() override;
+};
diff --git a/api/logic/minecraft/ftb/FTBVersion.h b/api/logic/minecraft/ftb/FTBVersion.h
new file mode 100644
index 00000000..805319b4
--- /dev/null
+++ b/api/logic/minecraft/ftb/FTBVersion.h
@@ -0,0 +1,32 @@
+#pragma once
+#include <minecraft/MinecraftVersion.h>
+
+class FTBVersion : public BaseVersion
+{
+public:
+ FTBVersion(MinecraftVersionPtr parent) : m_version(parent){};
+
+public:
+ virtual QString descriptor() override
+ {
+ return m_version->descriptor();
+ }
+
+ virtual QString name() override
+ {
+ return m_version->name();
+ }
+
+ virtual QString typeString() const override
+ {
+ return m_version->typeString();
+ }
+
+ MinecraftVersionPtr getMinecraftVersion()
+ {
+ return m_version;
+ }
+
+private:
+ MinecraftVersionPtr m_version;
+};
diff --git a/api/logic/minecraft/ftb/LegacyFTBInstance.cpp b/api/logic/minecraft/ftb/LegacyFTBInstance.cpp
new file mode 100644
index 00000000..a7091f1d
--- /dev/null
+++ b/api/logic/minecraft/ftb/LegacyFTBInstance.cpp
@@ -0,0 +1,27 @@
+#include "LegacyFTBInstance.h"
+#include <settings/INISettingsObject.h>
+#include <QDir>
+
+LegacyFTBInstance::LegacyFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) :
+ LegacyInstance(globalSettings, settings, rootDir)
+{
+}
+
+QString LegacyFTBInstance::id() const
+{
+ return "FTB/" + BaseInstance::id();
+}
+
+void LegacyFTBInstance::copy(const QDir &newDir)
+{
+ // set the target instance to be plain Legacy
+ INISettingsObject settings_obj(newDir.absoluteFilePath("instance.cfg"));
+ settings_obj.registerSetting("InstanceType", "Legacy");
+ QString inst_type = settings_obj.get("InstanceType").toString();
+ settings_obj.set("InstanceType", "Legacy");
+}
+
+QString LegacyFTBInstance::typeName() const
+{
+ return tr("Legacy FTB");
+}
diff --git a/api/logic/minecraft/ftb/LegacyFTBInstance.h b/api/logic/minecraft/ftb/LegacyFTBInstance.h
new file mode 100644
index 00000000..7178bca4
--- /dev/null
+++ b/api/logic/minecraft/ftb/LegacyFTBInstance.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "minecraft/legacy/LegacyInstance.h"
+
+class LegacyFTBInstance : public LegacyInstance
+{
+ Q_OBJECT
+public:
+ explicit LegacyFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
+ virtual QString id() const;
+ virtual void copy(const QDir &newDir);
+ virtual QString typeName() const;
+ bool canExport() const override
+ {
+ return false;
+ }
+};
diff --git a/api/logic/minecraft/ftb/OneSixFTBInstance.cpp b/api/logic/minecraft/ftb/OneSixFTBInstance.cpp
new file mode 100644
index 00000000..81e939a1
--- /dev/null
+++ b/api/logic/minecraft/ftb/OneSixFTBInstance.cpp
@@ -0,0 +1,138 @@
+#include "OneSixFTBInstance.h"
+#include "FTBProfileStrategy.h"
+
+#include "minecraft/MinecraftProfile.h"
+#include "minecraft/GradleSpecifier.h"
+#include "tasks/SequentialTask.h"
+#include <settings/INISettingsObject.h>
+#include <FileSystem.h>
+
+#include <QJsonArray>
+
+OneSixFTBInstance::OneSixFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir) :
+ OneSixInstance(globalSettings, settings, rootDir)
+{
+ m_globalSettings = globalSettings;
+}
+
+void OneSixFTBInstance::copy(const QDir &newDir)
+{
+ QStringList libraryNames;
+ // create patch file
+ {
+ qDebug()<< "Creating patch file for FTB instance...";
+ QFile f(minecraftRoot() + "/pack.json");
+ if (!f.open(QFile::ReadOnly))
+ {
+ qCritical() << "Couldn't open" << f.fileName() << ":" << f.errorString();
+ return;
+ }
+ QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
+ QJsonArray libs = root.value("libraries").toArray();
+ QJsonArray outLibs;
+ for (auto lib : libs)
+ {
+ QJsonObject libObj = lib.toObject();
+ libObj.insert("MMC-hint", QString("local"));
+ libObj.insert("insert", QString("prepend"));
+ libraryNames.append(libObj.value("name").toString());
+ outLibs.append(libObj);
+ }
+ root.remove("libraries");
+ root.remove("id");
+
+ // HACK HACK HACK HACK
+ // A workaround for a problem in MultiMC, triggered by a historical problem in FTB,
+ // triggered by Mojang getting their library versions wrong in 1.7.10
+ if(intendedVersionId() == "1.7.10")
+ {
+ auto insert = [&outLibs, &libraryNames](QString name)
+ {
+ QJsonObject libObj;
+ libObj.insert("insert", QString("replace"));
+ libObj.insert("name", name);
+ libraryNames.push_back(name);
+ outLibs.prepend(libObj);
+ };
+ insert("com.google.guava:guava:16.0");
+ insert("org.apache.commons:commons-lang3:3.2.1");
+ }
+ root.insert("+libraries", outLibs);
+ root.insert("order", 1);
+ root.insert("fileId", QString("org.multimc.ftb.pack.json"));
+ root.insert("name", name());
+ root.insert("mcVersion", intendedVersionId());
+ root.insert("version", intendedVersionId());
+ FS::ensureFilePathExists(newDir.absoluteFilePath("patches/ftb.json"));
+ QFile out(newDir.absoluteFilePath("patches/ftb.json"));
+ if (!out.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ qCritical() << "Couldn't open" << out.fileName() << ":" << out.errorString();
+ return;
+ }
+ out.write(QJsonDocument(root).toJson());
+ }
+ // copy libraries
+ {
+ qDebug() << "Copying FTB libraries";
+ for (auto library : libraryNames)
+ {
+ GradleSpecifier lib(library);
+ const QString out = QDir::current().absoluteFilePath("libraries/" + lib.toPath());
+ if (QFile::exists(out))
+ {
+ continue;
+ }
+ if (!FS::ensureFilePathExists(out))
+ {
+ qCritical() << "Couldn't create folder structure for" << out;
+ }
+ if (!QFile::copy(librariesPath().absoluteFilePath(lib.toPath()), out))
+ {
+ qCritical() << "Couldn't copy" << QString(lib);
+ }
+ }
+ }
+ // now set the target instance to be plain OneSix
+ INISettingsObject settings_obj(newDir.absoluteFilePath("instance.cfg"));
+ settings_obj.registerSetting("InstanceType", "Legacy");
+ QString inst_type = settings_obj.get("InstanceType").toString();
+ settings_obj.set("InstanceType", "OneSix");
+}
+
+QString OneSixFTBInstance::id() const
+{
+ return "FTB/" + BaseInstance::id();
+}
+
+QDir OneSixFTBInstance::librariesPath() const
+{
+ return QDir(m_globalSettings->get("FTBRoot").toString() + "/libraries");
+}
+
+QDir OneSixFTBInstance::versionsPath() const
+{
+ return QDir(m_globalSettings->get("FTBRoot").toString() + "/versions");
+}
+
+bool OneSixFTBInstance::providesVersionFile() const
+{
+ return true;
+}
+
+void OneSixFTBInstance::createProfile()
+{
+ m_profile.reset(new MinecraftProfile(new FTBProfileStrategy(this)));
+}
+
+std::shared_ptr<Task> OneSixFTBInstance::createUpdateTask()
+{
+ return OneSixInstance::createUpdateTask();
+}
+
+QString OneSixFTBInstance::typeName() const
+{
+ return tr("OneSix FTB");
+}
+
+#include "OneSixFTBInstance.moc"
diff --git a/api/logic/minecraft/ftb/OneSixFTBInstance.h b/api/logic/minecraft/ftb/OneSixFTBInstance.h
new file mode 100644
index 00000000..e7f8f485
--- /dev/null
+++ b/api/logic/minecraft/ftb/OneSixFTBInstance.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "minecraft/onesix/OneSixInstance.h"
+
+class OneSixFTBInstance : public OneSixInstance
+{
+ Q_OBJECT
+public:
+ explicit OneSixFTBInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
+ virtual ~OneSixFTBInstance(){};
+
+ void copy(const QDir &newDir) override;
+
+ virtual void createProfile() override;
+
+ virtual std::shared_ptr<Task> createUpdateTask() override;
+
+ virtual QString id() const override;
+
+ QDir librariesPath() const override;
+ QDir versionsPath() const override;
+ bool providesVersionFile() const override;
+ virtual QString typeName() const override;
+ bool canExport() const override
+ {
+ return false;
+ }
+private:
+ SettingsObjectPtr m_globalSettings;
+};