From 82c87aa06f793b9f38e6cb42d284f00695f4bac5 Mon Sep 17 00:00:00 2001 From: Jan Dalheimer Date: Fri, 20 Dec 2013 14:47:26 +0100 Subject: [PATCH] Initial FTB support. Allows "tracking" of FTB instances. --- CMakeLists.txt | 8 ++ MultiMC.cpp | 49 ++++++++++ gui/dialogs/SettingsDialog.cpp | 36 ++++++++ gui/dialogs/SettingsDialog.h | 4 + gui/dialogs/SettingsDialog.ui | 81 ++++++++++++++++ logic/InstanceFactory.cpp | 86 ++++++++++++----- logic/InstanceFactory.h | 11 ++- logic/LegacyFTBInstance.cpp | 16 ++++ logic/LegacyFTBInstance.h | 13 +++ logic/OneSixFTBInstance.cpp | 111 ++++++++++++++++++++++ logic/OneSixFTBInstance.h | 20 ++++ logic/lists/ForgeVersionList.cpp | 1 + logic/lists/InstanceList.cpp | 153 +++++++++++++++++++++++-------- logic/lists/InstanceList.h | 14 ++- logic/tasks/SequentialTask.cpp | 77 ++++++++++++++++ logic/tasks/SequentialTask.h | 32 +++++++ 16 files changed, 644 insertions(+), 68 deletions(-) create mode 100644 logic/LegacyFTBInstance.cpp create mode 100644 logic/LegacyFTBInstance.h create mode 100644 logic/OneSixFTBInstance.cpp create mode 100644 logic/OneSixFTBInstance.h create mode 100644 logic/tasks/SequentialTask.cpp create mode 100644 logic/tasks/SequentialTask.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 17674513..7b371aa9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,12 @@ logic/ForgeInstaller.cpp logic/NostalgiaInstance.h logic/NostalgiaInstance.cpp +# FTB +logic/OneSixFTBInstance.h +logic/OneSixFTBInstance.cpp +logic/LegacyFTBInstance.h +logic/LegacyFTBInstance.cpp + # Lists logic/lists/InstanceList.h logic/lists/InstanceList.cpp @@ -385,6 +391,8 @@ logic/EnabledItemFilter.cpp logic/tasks/ProgressProvider.h logic/tasks/Task.h logic/tasks/Task.cpp +logic/tasks/SequentialTask.h +logic/tasks/SequentialTask.cpp # Utilities logic/JavaChecker.h diff --git a/MultiMC.cpp b/MultiMC.cpp index 5d08af4c..8d188e96 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -313,6 +313,55 @@ void MultiMC::initGlobalSettings() m_settings->registerSetting(new Setting("UseDevBuilds", false)); m_settings->registerSetting(new Setting("AutoUpdate", true)); + // FTB + m_settings->registerSetting(new Setting("TrackFTBInstances", false)); + m_settings->registerSetting(new Setting("FTBLauncherRoot", + #ifdef Q_OS_LINUX + QDir::home().absoluteFilePath(".ftblauncher") + #elif defined(Q_OS_WIN32) + PathCombine(QDir::homePath(), "AppData/Roaming/ftblauncher") + #elif defined(Q_OS_MAC) + PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher") + #endif + )); + + m_settings->registerSetting(new Setting("FTBRoot")); + if (m_settings->get("FTBRoot").isNull()) + { + QString ftbRoot; + QFile f(QDir(m_settings->get("FTBLauncherRoot").toString()).absoluteFilePath("ftblaunch.cfg")); + QLOG_INFO() << "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()) + { + QLOG_INFO() << "Failed to get FTB root path"; + } + else + { + QLOG_INFO() << "FTB is installed at" << ftbRoot; + m_settings->set("FTBRoot", ftbRoot); + } + } + else + { + QLOG_WARN() << "Couldn't open" << f.fileName() << ":" << f.errorString(); + QLOG_WARN() << "This is perfectly normal if you don't have FTB installed"; + } + } + // Folders m_settings->registerSetting(new Setting("InstanceDir", "instances")); m_settings->registerSetting(new Setting("CentralModsDir", "mods")); diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp index b960483a..131cb5c3 100644 --- a/gui/dialogs/SettingsDialog.cpp +++ b/gui/dialogs/SettingsDialog.cpp @@ -60,6 +60,32 @@ void SettingsDialog::updateCheckboxStuff() ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked()); } +void SettingsDialog::on_ftbLauncherBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Launcher Directory"), + ui->ftbLauncherBox->text()); + QString cooked_dir = NormalizePath(raw_dir); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists()) + { + ui->ftbLauncherBox->setText(cooked_dir); + } +} + +void SettingsDialog::on_ftbBrowseBtn_clicked() +{ + QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Directory"), + ui->ftbBox->text()); + QString cooked_dir = NormalizePath(raw_dir); + + // do not allow current dir - it's dirty. Do not allow dirs that don't exist + if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists()) + { + ui->ftbBox->setText(cooked_dir); + } +} + void SettingsDialog::on_instDirBrowseBtn_clicked() { QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Instance Directory"), @@ -135,6 +161,11 @@ void SettingsDialog::applySettings(SettingsObject *s) // Updates s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked()); + // FTB + s->set("TrackFTBInstances", ui->trackFtbBox->isChecked()); + s->set("FTBLauncherRoot", ui->ftbLauncherBox->text()); + s->set("FTBRoot", ui->ftbBox->text()); + // Folders // TODO: Offer to move instances to new instance folder. s->set("InstanceDir", ui->instDirTextBox->text()); @@ -185,6 +216,11 @@ void SettingsDialog::loadSettings(SettingsObject *s) ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool()); ui->devBuildsCheckBox->setChecked(s->get("UseDevBuilds").toBool()); + // FTB + ui->trackFtbBox->setChecked(s->get("TrackFTBInstances").toBool()); + ui->ftbLauncherBox->setText(s->get("FTBLauncherRoot").toString()); + ui->ftbBox->setText(s->get("FTBRoot").toString()); + // Folders ui->instDirTextBox->setText(s->get("InstanceDir").toString()); ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); diff --git a/gui/dialogs/SettingsDialog.h b/gui/dialogs/SettingsDialog.h index 0cb8fa38..36fc4797 100644 --- a/gui/dialogs/SettingsDialog.h +++ b/gui/dialogs/SettingsDialog.h @@ -45,6 +45,10 @@ protected: private slots: + void on_ftbLauncherBrowseBtn_clicked(); + + void on_ftbBrowseBtn_clicked(); + void on_instDirBrowseBtn_clicked(); void on_modsDirBrowseBtn_clicked(); diff --git a/gui/dialogs/SettingsDialog.ui b/gui/dialogs/SettingsDialog.ui index 17320b48..4d06d1f8 100644 --- a/gui/dialogs/SettingsDialog.ui +++ b/gui/dialogs/SettingsDialog.ui @@ -95,6 +95,87 @@ + + + + FTB + + + + + + false + + + + 0 + 0 + + + + + 28 + 16777215 + + + + Qt::TabFocus + + + ... + + + + + + + Launcher: + + + + + + + false + + + + + + + Track FTB instances + + + + + + + + + + true + + + + 28 + 16777215 + + + + ... + + + + + + + Files: + + + + + + diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index 66b271d0..31a287dd 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -20,7 +20,9 @@ #include "BaseInstance.h" #include "LegacyInstance.h" +#include "LegacyFTBInstance.h" #include "OneSixInstance.h" +#include "OneSixFTBInstance.h" #include "NostalgiaInstance.h" #include "BaseVersion.h" #include "MinecraftVersion.h" @@ -60,6 +62,14 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst { inst = new NostalgiaInstance(instDir, m_settings, this); } + else if (inst_type == "LegacyFTB") + { + inst = new LegacyFTBInstance(instDir, m_settings, this); + } + else if (inst_type == "OneSixFTB") + { + inst = new OneSixFTBInstance(instDir, m_settings, this); + } else { return InstanceFactory::UnknownLoadError; @@ -69,7 +79,8 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *&inst, BaseVersionPtr version, - const QString &instDir) + const QString &instDir, + const InstType type) { QDir rootDir(instDir); @@ -85,32 +96,63 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance(BaseInstance *& auto m_settings = new INISettingsObject(PathCombine(instDir, "instance.cfg")); m_settings->registerSetting(new Setting("InstanceType", "Legacy")); - switch (mcVer->type) + if (type == NormalInst) { - case MinecraftVersion::Legacy: - m_settings->set("InstanceType", "Legacy"); - inst = new LegacyInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor()); - inst->setShouldUseCustomBaseJar(false); - break; - case MinecraftVersion::OneSix: - m_settings->set("InstanceType", "OneSix"); - inst = new OneSixInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor()); - inst->setShouldUseCustomBaseJar(false); - break; - case MinecraftVersion::Nostalgia: - m_settings->set("InstanceType", "Nostalgia"); - inst = new NostalgiaInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor()); - inst->setShouldUseCustomBaseJar(false); - break; - default: + switch (mcVer->type) + { + case MinecraftVersion::Legacy: + m_settings->set("InstanceType", "Legacy"); + inst = new LegacyInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSix"); + inst = new OneSixInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + case MinecraftVersion::Nostalgia: + m_settings->set("InstanceType", "Nostalgia"); + inst = new NostalgiaInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + default: + { + delete m_settings; + return InstanceFactory::NoSuchVersion; + } + } + } + else if (type == FTBInstance) + { + switch (mcVer->type) + { + case MinecraftVersion::Legacy: + m_settings->set("InstanceType", "LegacyFTB"); + inst = new LegacyFTBInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + case MinecraftVersion::OneSix: + m_settings->set("InstanceType", "OneSixFTB"); + inst = new OneSixFTBInstance(instDir, m_settings, this); + inst->setIntendedVersionId(version->descriptor()); + inst->setShouldUseCustomBaseJar(false); + break; + default: + { + delete m_settings; + return InstanceFactory::NoSuchVersion; + } + } + } + else { delete m_settings; return InstanceFactory::NoSuchVersion; } - } // FIXME: really, how do you even know? return InstanceFactory::NoCreateError; diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h index 01e5af7e..5ff4c7ec 100644 --- a/logic/InstanceFactory.h +++ b/logic/InstanceFactory.h @@ -55,18 +55,25 @@ public: CantCreateDir }; + enum InstType + { + NormalInst, + FTBInstance + }; + /*! * \brief Creates a stub instance * * \param inst Pointer to store the created instance in. - * \param inst Game version to use for the instance + * \param version Game version to use for the instance * \param instDir The new instance's directory. + * \param type The type of instance to create * \return An InstCreateError error code. * - InstExists if the given instance directory is already an instance. * - CantCreateDir if the given instance directory cannot be created. */ InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version, - const QString &instDir); + const QString &instDir, const InstType type = NormalInst); /*! * \brief Creates a copy of an existing instance with a new name diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp new file mode 100644 index 00000000..84d5a900 --- /dev/null +++ b/logic/LegacyFTBInstance.cpp @@ -0,0 +1,16 @@ +#include "LegacyFTBInstance.h" + +LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + LegacyInstance(rootDir, settings, parent) +{ +} + +QString LegacyFTBInstance::getStatusbarDescription() +{ + return "Legacy FTB: " + intendedVersionId(); +} + +bool LegacyFTBInstance::menuActionEnabled(QString action_name) const +{ + return false; +} diff --git a/logic/LegacyFTBInstance.h b/logic/LegacyFTBInstance.h new file mode 100644 index 00000000..2ae72797 --- /dev/null +++ b/logic/LegacyFTBInstance.h @@ -0,0 +1,13 @@ +#pragma once + +#include "LegacyInstance.h" + +class LegacyFTBInstance : public LegacyInstance +{ + Q_OBJECT +public: + explicit LegacyFTBInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const; +}; diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp new file mode 100644 index 00000000..567004b9 --- /dev/null +++ b/logic/OneSixFTBInstance.cpp @@ -0,0 +1,111 @@ +#include "OneSixFTBInstance.h" + +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include "tasks/SequentialTask.h" +#include "ForgeInstaller.h" +#include "lists/ForgeVersionList.h" +#include "MultiMC.h" + +class OneSixFTBInstanceForge : public Task +{ + Q_OBJECT +public: + explicit OneSixFTBInstanceForge(const QString &version, OneSixFTBInstance *inst, QObject *parent = 0) : + Task(parent), instance(inst), version("Forge " + version) + { + } + + void executeTask() + { + for (int i = 0; i < MMC->forgelist()->count(); ++i) + { + if (MMC->forgelist()->at(i)->name() == version) + { + forgeVersion = std::dynamic_pointer_cast(MMC->forgelist()->at(i)); + break; + } + } + if (!forgeVersion) + return; + entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); + if (entry->stale) + { + setStatus(tr("Downloading Forge...")); + fjob = new NetJob("Forge download"); + fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry)); + connect(fjob, &NetJob::failed, [this](){emitFailed(m_failReason);}); + connect(fjob, &NetJob::succeeded, this, &OneSixFTBInstanceForge::installForge); + connect(fjob, &NetJob::progress, [this](qint64 c, qint64 total){ setProgress(100 * c / total); }); + fjob->start(); + } + else + { + installForge(); + } + } + +private +slots: + void installForge() + { + setStatus(tr("Installing Forge...")); + QString forgePath = entry->getFullPath(); + ForgeInstaller forge(forgePath, forgeVersion->universal_url); + if (!instance->reloadFullVersion()) + { + emitFailed(tr("Couldn't load the version config")); + return; + } + if (!forge.apply(instance->getFullVersion())) + { + emitFailed(tr("Couldn't install Forge")); + return; + } + emitSucceeded(); + } + +private: + OneSixFTBInstance *instance; + QString version; + ForgeVersionPtr forgeVersion; + MetaEntryPtr entry; + NetJob *fjob; +}; + +OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, QObject *parent) : + OneSixInstance(rootDir, settings, parent) +{ + QFile f(QDir(minecraftRoot()).absoluteFilePath("pack.json")); + if (f.open(QFile::ReadOnly)) + { + QString data = QString::fromUtf8(f.readAll()); + QRegularExpressionMatch match = QRegularExpression("net.minecraftforge:minecraftforge:[\\.\\d]*").match(data); + m_forge.reset(new OneSixLibrary(match.captured())); + m_forge->finalize(); + } +} + +QString OneSixFTBInstance::getStatusbarDescription() +{ + return "OneSix FTB: " + intendedVersionId(); +} +bool OneSixFTBInstance::menuActionEnabled(QString action_name) const +{ + return false; +} + +std::shared_ptr OneSixFTBInstance::doUpdate(bool only_prepare) +{ + std::shared_ptr task; + task.reset(new SequentialTask(this)); + if (!MMC->forgelist()->isLoaded()) + { + task->addTask(std::shared_ptr(MMC->forgelist()->getLoadTask())); + } + task->addTask(OneSixInstance::doUpdate(only_prepare)); + task->addTask(std::shared_ptr(new OneSixFTBInstanceForge(m_forge->version(), this, this))); + return task; +} + +#include "OneSixFTBInstance.moc" diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h new file mode 100644 index 00000000..7600090c --- /dev/null +++ b/logic/OneSixFTBInstance.h @@ -0,0 +1,20 @@ +#pragma once + +#include "OneSixInstance.h" + +class OneSixLibrary; + +class OneSixFTBInstance : public OneSixInstance +{ + Q_OBJECT +public: + explicit OneSixFTBInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const; + + virtual std::shared_ptr doUpdate(bool only_prepare) override; + +private: + std::shared_ptr m_forge; +}; diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index b5e421af..d6d353da 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -159,6 +159,7 @@ ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() void ForgeListLoadTask::executeTask() { + setStatus(tr("Fetching Forge version list")); auto job = new NetJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 15fd10ba..539413d8 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -22,11 +22,14 @@ #include #include #include +#include +#include #include #include "MultiMC.h" #include "logic/lists/InstanceList.h" #include "logic/lists/IconList.h" +#include "logic/lists/MinecraftVersionList.h" #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logger/QsLog.h" @@ -42,6 +45,8 @@ InstanceList::InstanceList(const QString &instDir, QObject *parent) { QDir::current().mkpath(m_instDir); } + + connect(MMC->minecraftlist().get(), &MinecraftVersionList::modelReset, this, &InstanceList::loadList); } InstanceList::~InstanceList() @@ -285,57 +290,87 @@ InstanceList::InstListError InstanceList::loadList() beginResetModel(); m_instances.clear(); - QDir dir(m_instDir); - QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, - QDirIterator::FollowSymlinks); - while (iter.hasNext()) + { - QString subDir = iter.next(); - if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists()) - continue; - - BaseInstance *instPtr = NULL; - auto &loader = InstanceFactory::get(); - auto error = loader.loadInstance(instPtr, subDir); - - if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) + QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable, + QDirIterator::FollowSymlinks); + while (iter.hasNext()) { - QString errorMsg = QString("Failed to load instance %1: ") - .arg(QFileInfo(subDir).baseName()) - .toUtf8(); + QString subDir = iter.next(); + if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists()) + continue; - switch (error) - { - default: - errorMsg += QString("Unknown instance loader error %1").arg(error); - break; - } - QLOG_ERROR() << errorMsg.toUtf8(); + BaseInstance *instPtr = NULL; + auto error = InstanceFactory::get().loadInstance(instPtr, subDir); + continueProcessInstance(instPtr, error, subDir, groupMap); } - else if (!instPtr) + } + + if (MMC->settings()->get("TrackFTBInstances").toBool() && MMC->minecraftlist()->isLoaded()) + { + QDir dir = QDir(MMC->settings()->get("FTBLauncherRoot").toString()); + QDir dataDir = QDir(MMC->settings()->get("FTBRoot").toString()); + if (!dir.exists()) { - QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") - .arg(QFileInfo(subDir).baseName()) - .toUtf8(); + QLOG_INFO() << "The FTB launcher directory specified does not exist. Please check your settings."; + } + else if (!dataDir.exists()) + { + QLOG_INFO() << "The FTB directory specified does not exist. Please check your settings"; } else { - std::shared_ptr inst(instPtr); - auto iter = groupMap.find(inst->id()); - if (iter != groupMap.end()) + dir.cd("ModPacks"); + QFile f(dir.absoluteFilePath("modpacks.xml")); + if (f.open(QFile::ReadOnly)) { - inst->setGroupInitial((*iter)); + QXmlStreamReader reader(&f); + while (!reader.atEnd()) + { + switch (reader.readNext()) + { + case QXmlStreamReader::StartElement: + { + if (reader.name() == "modpack") + { + QXmlStreamAttributes attrs = reader.attributes(); + const QDir instanceDir = QDir(dataDir.absoluteFilePath(attrs.value("dir").toString())); + if (instanceDir.exists()) + { + const QString name = attrs.value("name").toString(); + const QString iconKey = attrs.value("logo").toString().remove(QRegularExpression("\\..*")); + const QString mcVersion = attrs.value("mcVersion").toString(); + const QString notes = attrs.value("description").toString(); + QLOG_DEBUG() << dir.absoluteFilePath(attrs.value("logo").toString()); + MMC->icons()->addIcon(iconKey, iconKey, dir.absoluteFilePath(attrs.value("dir").toString() + QDir::separator() + attrs.value("logo").toString()), true); + + BaseInstance *instPtr = NULL; + auto error = InstanceFactory::get().createInstance(instPtr, MMC->minecraftlist()->findVersion(mcVersion), instanceDir.absolutePath(), InstanceFactory::FTBInstance); + if (instPtr && error == InstanceFactory::NoCreateError) + { + instPtr->setGroupInitial("FTB"); + instPtr->setName(name); + instPtr->setIconKey(iconKey); + instPtr->setIntendedVersionId(mcVersion); + instPtr->setNotes(notes); + } + continueProcessInstance(instPtr, error, instanceDir, groupMap); + } + } + break; + } + case QXmlStreamReader::EndElement: + break; + case QXmlStreamReader::Characters: + break; + default: + break; + } + } } - QLOG_INFO() << "Loaded instance " << inst->name(); - inst->setParent(this); - m_instances.append(inst); - connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this, - SLOT(propertiesChanged(BaseInstance *))); - connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged())); - connect(instPtr, SIGNAL(nuked(BaseInstance *)), this, - SLOT(instanceNuked(BaseInstance *))); } } + endResetModel(); emit dataIsInvalid(); return NoError; @@ -409,6 +444,46 @@ int InstanceList::getInstIndex(BaseInstance *inst) const return -1; } +void InstanceList::continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap) +{ + if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) + { + QString errorMsg = QString("Failed to load instance %1: ") + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); + + switch (error) + { + default: + errorMsg += QString("Unknown instance loader error %1").arg(error); + break; + } + QLOG_ERROR() << errorMsg.toUtf8(); + } + else if (!instPtr) + { + QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") + .arg(QFileInfo(dir.absolutePath()).baseName()) + .toUtf8(); + } + else + { + auto iter = groupMap.find(instPtr->id()); + if (iter != groupMap.end()) + { + instPtr->setGroupInitial((*iter)); + } + QLOG_INFO() << "Loaded instance " << instPtr->name(); + instPtr->setParent(this); + m_instances.append(std::shared_ptr(instPtr)); + connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this, + SLOT(propertiesChanged(BaseInstance *))); + connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged())); + connect(instPtr, SIGNAL(nuked(BaseInstance *)), this, + SLOT(instanceNuked(BaseInstance *))); + } +} + void InstanceList::instanceNuked(BaseInstance *inst) { int i = getInstIndex(inst); diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index f23b7763..b3ee6cfe 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -25,6 +25,8 @@ class BaseInstance; +class QDir; + class InstanceList : public QAbstractListModel { Q_OBJECT @@ -65,11 +67,6 @@ public: return m_instDir; } - /*! - * \brief Loads the instance list. Triggers notifications. - */ - InstListError loadList(); - /*! * \brief Get the instance at index */ @@ -108,6 +105,11 @@ public slots: void on_InstFolderChanged(const Setting &setting, QVariant value); + /*! + * \brief Loads the instance list. Triggers notifications. + */ + InstListError loadList(); + private slots: void propertiesChanged(BaseInstance *inst); @@ -117,6 +119,8 @@ slots: private: int getInstIndex(BaseInstance *inst) const; + void continueProcessInstance(BaseInstance *instPtr, const int error, const QDir &dir, QMap &groupMap); + protected: QString m_instDir; QList m_instances; diff --git a/logic/tasks/SequentialTask.cpp b/logic/tasks/SequentialTask.cpp new file mode 100644 index 00000000..63025eee --- /dev/null +++ b/logic/tasks/SequentialTask.cpp @@ -0,0 +1,77 @@ +#include "SequentialTask.h" + +SequentialTask::SequentialTask(QObject *parent) : + Task(parent), m_currentIndex(-1) +{ + +} + +QString SequentialTask::getStatus() const +{ + if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) + { + return QString(); + } + return m_queue.at(m_currentIndex)->getStatus(); +} + +void SequentialTask::getProgress(qint64 ¤t, qint64 &total) +{ + current = 0; + total = 0; + for (int i = 0; i < m_queue.size(); ++i) + { + qint64 subCurrent, subTotal; + m_queue.at(i)->getProgress(subCurrent, subTotal); + current += subCurrent; + total += subTotal; + } +} + +void SequentialTask::addTask(std::shared_ptr task) +{ + m_queue.append(task); +} + +void SequentialTask::executeTask() +{ + m_currentIndex = -1; + startNext(); +} + +void SequentialTask::startNext() +{ + if (m_currentIndex != -1) + { + std::shared_ptr previous = m_queue[m_currentIndex]; + disconnect(previous.get(), 0, this, 0); + } + m_currentIndex++; + if (m_queue.isEmpty() || m_currentIndex >= m_queue.size()) + { + emitSucceeded(); + return; + } + std::shared_ptr next = m_queue[m_currentIndex]; + connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString))); + connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString))); + connect(next.get(), SIGNAL(progress(qint64,qint64)), this, SLOT(subTaskProgress())); + connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext())); + next->start(); + emit status(getStatus()); +} + +void SequentialTask::subTaskFailed(const QString &msg) +{ + emitFailed(msg); +} +void SequentialTask::subTaskStatus(const QString &msg) +{ + setStatus(msg); +} +void SequentialTask::subTaskProgress() +{ + qint64 current, total; + getProgress(current, total); + setProgress(100 * current / total); +} diff --git a/logic/tasks/SequentialTask.h b/logic/tasks/SequentialTask.h new file mode 100644 index 00000000..7f046928 --- /dev/null +++ b/logic/tasks/SequentialTask.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Task.h" + +#include +#include + +class SequentialTask : public Task +{ + Q_OBJECT +public: + explicit SequentialTask(QObject *parent = 0); + + virtual QString getStatus() const; + virtual void getProgress(qint64 ¤t, qint64 &total); + + void addTask(std::shared_ptr task); + +protected: + void executeTask(); + +private +slots: + void startNext(); + void subTaskFailed(const QString &msg); + void subTaskStatus(const QString &msg); + void subTaskProgress(); + +private: + QQueue > m_queue; + int m_currentIndex; +};