summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp120
-rw-r--r--gui/dialogs/OneSixModEditDialog.ui9
-rw-r--r--gui/groupview/InstanceDelegate.cpp85
-rw-r--r--gui/groupview/InstanceDelegate.h6
-rw-r--r--logic/BaseInstaller.h6
-rw-r--r--logic/BaseInstance.cpp9
-rw-r--r--logic/BaseInstance.h8
-rw-r--r--logic/BaseInstance_p.h7
-rw-r--r--logic/ForgeInstaller.cpp105
-rw-r--r--logic/ForgeInstaller.h5
-rw-r--r--logic/LegacyFTBInstance.cpp2
-rw-r--r--logic/LegacyInstance.cpp4
-rw-r--r--logic/LiteLoaderInstaller.cpp68
-rw-r--r--logic/LiteLoaderInstaller.h5
-rw-r--r--logic/MinecraftProcess.cpp37
-rw-r--r--logic/MinecraftProcess.h1
-rw-r--r--logic/NostalgiaInstance.cpp2
-rw-r--r--logic/OneSixFTBInstance.cpp2
-rw-r--r--logic/OneSixInstance.cpp8
-rw-r--r--logic/OneSixVersionBuilder.cpp13
-rw-r--r--logic/VersionFinal.cpp157
-rw-r--r--logic/VersionFinal.h16
-rw-r--r--resources/instances/broken.pngbin0 -> 1139 bytes
-rw-r--r--resources/instances/instances.qrc11
-rw-r--r--resources/instances/kitten.pngbin0 -> 1605 bytes
25 files changed, 546 insertions, 140 deletions
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index 78585a05..2d459001 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -42,16 +42,6 @@
#include "logic/LiteLoaderInstaller.h"
#include "logic/OneSixVersionBuilder.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;
-}
-
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
{
@@ -155,34 +145,62 @@ void OneSixModEditDialog::on_removeLibraryBtn_clicked()
{
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
}
- else
- {
- reloadInstanceVersion();
- }
}
}
void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
{
- // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+ try
+ {
+ m_version->resetOrder();
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
}
void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
{
- // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+ if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+ {
+ return;
+ }
+ try
+ {
+ const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+ const int newRow = 0;m_version->move(row, VersionFinal::MoveUp);
+ //ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
}
void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
{
- // FIXME: IMPLEMENT LOGIC IN MODEL. SEE LEGACY DIALOG FOR EXAMPLE(S).
+ if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
+ {
+ return;
+ }
+ try
+ {
+ const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
+ const int newRow = 0;m_version->move(row, VersionFinal::MoveDown);
+ //ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
+ }
+ catch (MMCError &e)
+ {
+ QMessageBox::critical(this, tr("Error"), e.cause());
+ }
}
void OneSixModEditDialog::on_forgeBtn_clicked()
{
// FIXME: use actual model, not reloading. Move logic to model.
- // FIXME: model::isCustom();
- if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+ if (m_version->isCustom())
{
if (QMessageBox::question(this, tr("Revert?"),
tr("This action will remove your custom.json. Continue?")) !=
@@ -190,8 +208,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
{
return;
}
- // FIXME: model::revertToBase();
- QDir(m_inst->instanceRoot()).remove("custom.json");
+ m_version->revertToBase();
reloadInstanceVersion();
}
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
@@ -200,50 +217,14 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
- ForgeVersionPtr forgeVersion =
- std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
- if (!forgeVersion)
- return;
- auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
- if (entry->stale)
- {
- NetJob *fjob = new NetJob("Forge download");
- fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
- ProgressDialog dlg(this);
- dlg.exec(fjob);
- if (dlg.result() == QDialog::Accepted)
- {
- // install
- QString forgePath = entry->getFullPath();
- ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!forge.add(m_inst))
- {
- QLOG_ERROR() << "Failure installing forge";
- }
- }
- else
- {
- // failed to download forge :/
- }
- }
- else
- {
- // install
- QString forgePath = entry->getFullPath();
- ForgeInstaller forge(forgePath, forgeVersion->universal_url);
- if (!forge.add(m_inst))
- {
- QLOG_ERROR() << "Failure installing forge";
- }
- }
+ ProgressDialog dialog(this);
+ dialog.exec(ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
}
- reloadInstanceVersion();
}
void OneSixModEditDialog::on_liteloaderBtn_clicked()
{
- // FIXME: model...
- if (QDir(m_inst->instanceRoot()).exists("custom.json"))
+ if (m_version->isCustom())
{
if (QMessageBox::question(this, tr("Revert?"),
tr("This action will remove your custom.json. Continue?")) !=
@@ -251,7 +232,7 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
{
return;
}
- QDir(m_inst->instanceRoot()).remove("custom.json");
+ m_version->revertToBase();
reloadInstanceVersion();
}
VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
@@ -261,21 +242,8 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
- LiteLoaderVersionPtr liteloaderVersion =
- std::dynamic_pointer_cast<LiteLoaderVersion>(vselect.selectedVersion());
- if (!liteloaderVersion)
- return;
- LiteLoaderInstaller liteloader(liteloaderVersion);
- if (!liteloader.add(m_inst))
- {
- QMessageBox::critical(this, tr("LiteLoader"),
- tr("For reasons unknown, the LiteLoader installation failed. "
- "Check your MultiMC log files for details."));
- }
- else
- {
- reloadInstanceVersion();
- }
+ ProgressDialog dialog(this);
+ dialog.exec(LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
}
}
diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui
index b606dcd2..2c9f70bb 100644
--- a/gui/dialogs/OneSixModEditDialog.ui
+++ b/gui/dialogs/OneSixModEditDialog.ui
@@ -99,9 +99,6 @@
</item>
<item>
<widget class="QPushButton" name="moveLibraryUpBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="toolTip">
<string>This isn't implemented yet.</string>
</property>
@@ -112,9 +109,6 @@
</item>
<item>
<widget class="QPushButton" name="moveLibraryDownBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="toolTip">
<string>This isn't implemented yet.</string>
</property>
@@ -125,9 +119,6 @@
</item>
<item>
<widget class="QPushButton" name="resetLibraryOrderBtn">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="toolTip">
<string>This isn't implemented yet.</string>
</property>
diff --git a/gui/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp
index 8a273758..7ca4d7b4 100644
--- a/gui/groupview/InstanceDelegate.cpp
+++ b/gui/groupview/InstanceDelegate.cpp
@@ -18,9 +18,13 @@
#include <QTextOption>
#include <QTextLayout>
#include <QApplication>
-#include <QtCore/qmath.h>
+#include <QtMath>
#include "GroupView.h"
+#include "logic/BaseInstance.h"
+#include "logic/lists/InstanceList.h"
+
+QCache<QString, QPixmap> ListViewDelegate::m_pixmapCache;
// Origin: Qt
static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
@@ -45,8 +49,6 @@ static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &he
textLayout.endLayout();
}
-#define QFIXED_MAX (INT_MAX / 256)
-
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
{
}
@@ -108,6 +110,64 @@ void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option
painter->restore();
}
+void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseInstance *instance)
+{
+ QList<QString> pixmaps;
+ for (auto flag : instance->flags())
+ {
+ switch (flag)
+ {
+ case BaseInstance::VersionBrokenFlag:
+ pixmaps.append("broken");
+ break;
+ }
+ }
+
+ // begin easter eggs
+ if (instance->name().contains("btw", Qt::CaseInsensitive) ||
+ instance->name().contains("better then wolves", Qt::CaseInsensitive) ||
+ instance->name().contains("better than wolves", Qt::CaseInsensitive))
+ {
+ pixmaps.append("herobrine");
+ }
+ if (instance->name().contains("direwolf", Qt::CaseInsensitive))
+ {
+ pixmaps.append("enderman");
+ }
+ if (instance->name().contains("kitten", Qt::CaseInsensitive))
+ {
+ pixmaps.append("kitten");
+ }
+ if (instance->name().contains("derp", Qt::CaseInsensitive))
+ {
+ pixmaps.append("derp");
+ }
+ // end easter eggs
+
+ static const int itemSide = 24;
+ static const int spacing = 1;
+ const int itemsPerRow = qMax(1, qFloor(double(option.rect.width() + spacing) / double(itemSide + spacing)));
+ const int rows = qCeil((double)pixmaps.size() / (double)itemsPerRow);
+ QListIterator<QString> it(pixmaps);
+ painter->translate(option.rect.topLeft());
+ for (int y = 0; y < rows; ++y)
+ {
+ for (int x = 0; x < itemsPerRow; ++x)
+ {
+ if (!it.hasNext())
+ {
+ return;
+ }
+ const QPixmap pixmap = ListViewDelegate::requestPixmap(it.next()).scaled(
+ itemSide, itemSide, Qt::KeepAspectRatio, Qt::FastTransformation);
+ painter->drawPixmap(option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide,
+ y * itemSide + qMax(y - 1, 0) * spacing, itemSide, itemSide,
+ pixmap);
+ }
+ }
+ painter->translate(-option.rect.topLeft());
+}
+
static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option)
{
QStyle *style = option->widget ? option->widget->style() : QApplication::style();
@@ -257,8 +317,14 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
line.draw(painter, position);
}
- drawProgressOverlay(painter, opt,
- index.data(GroupViewRoles::ProgressValueRole).toInt(),
+ auto instance = (BaseInstance*)index.data(InstanceList::InstancePointerRole)
+ .value<void *>();
+ if (instance)
+ {
+ drawBadges(painter, opt, instance);
+ }
+
+ drawProgressOverlay(painter, opt, index.data(GroupViewRoles::ProgressValueRole).toInt(),
index.data(GroupViewRoles::ProgressMaximumRole).toInt());
painter->restore();
@@ -284,3 +350,12 @@ QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
QSize sz(100, height);
return sz;
}
+
+QPixmap ListViewDelegate::requestPixmap(const QString &key)
+{
+ if (!m_pixmapCache.contains(key))
+ {
+ m_pixmapCache.insert(key, new QPixmap(":/icons/badges/" + key + ".png"));
+ }
+ return *m_pixmapCache.object(key);
+}
diff --git a/gui/groupview/InstanceDelegate.h b/gui/groupview/InstanceDelegate.h
index de2f429b..9ab44864 100644
--- a/gui/groupview/InstanceDelegate.h
+++ b/gui/groupview/InstanceDelegate.h
@@ -16,14 +16,20 @@
#pragma once
#include <QStyledItemDelegate>
+#include <QCache>
class ListViewDelegate : public QStyledItemDelegate
{
public:
explicit ListViewDelegate(QObject *parent = 0);
+ static QPixmap requestPixmap(const QString &key);
+
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+private:
+ static QCache<QString, QPixmap> m_pixmapCache;
};
diff --git a/logic/BaseInstaller.h b/logic/BaseInstaller.h
index c572e004..d59833cc 100644
--- a/logic/BaseInstaller.h
+++ b/logic/BaseInstaller.h
@@ -20,6 +20,10 @@
class OneSixInstance;
class QDir;
class QString;
+class QObject;
+class ProgressProvider;
+class BaseVersion;
+typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
class BaseInstaller
{
@@ -31,6 +35,8 @@ public:
virtual bool add(OneSixInstance *to);
virtual bool remove(OneSixInstance *from);
+ virtual ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
+
protected:
virtual QString id() const = 0;
QString filename(const QString &root) const;
diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp
index c565ab59..c7b29548 100644
--- a/logic/BaseInstance.cpp
+++ b/logic/BaseInstance.cpp
@@ -37,7 +37,6 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir,
I_D(BaseInstance);
d->m_settings = settings_obj;
d->m_rootDir = rootDir;
- d->m_flags = 0;
settings().registerSetting("name", "Unnamed Instance");
settings().registerSetting("iconKey", "default");
@@ -147,13 +146,13 @@ SettingsObject &BaseInstance::settings() const
return *d->m_settings;
}
-BaseInstance::InstanceFlags BaseInstance::flags() const
+QSet<BaseInstance::InstanceFlag> BaseInstance::flags() const
{
I_D(const BaseInstance);
- return InstanceFlags(d->m_flags);
+ return QSet<InstanceFlag>(d->m_flags);
}
-void BaseInstance::setFlags(const BaseInstance::InstanceFlags flags)
+void BaseInstance::setFlags(const QSet<InstanceFlag> &flags)
{
I_D(BaseInstance);
if (flags != d->m_flags)
@@ -166,7 +165,7 @@ void BaseInstance::setFlags(const BaseInstance::InstanceFlags flags)
bool BaseInstance::canLaunch() const
{
- return !(flags() & VersionBrokenFlag);
+ return !flags().contains(VersionBrokenFlag);
}
bool BaseInstance::reload()
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index 195fd339..d38ae409 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -17,6 +17,7 @@
#include <QObject>
#include <QDateTime>
+#include <QSet>
#include <settingsobject.h>
@@ -184,9 +185,8 @@ public:
NoFlags = 0x00,
VersionBrokenFlag = 0x01
};
- Q_DECLARE_FLAGS(InstanceFlags, InstanceFlag)
- InstanceFlags flags() const;
- void setFlags(const BaseInstance::InstanceFlags flags);
+ QSet<InstanceFlag> flags() const;
+ void setFlags(const QSet<InstanceFlag> &flags);
bool canLaunch() const;
@@ -218,4 +218,4 @@ protected:
// pointer for lazy people
typedef std::shared_ptr<BaseInstance> InstancePtr;
-Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
+Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
diff --git a/logic/BaseInstance_p.h b/logic/BaseInstance_p.h
index 73eebec7..8cf3b27d 100644
--- a/logic/BaseInstance_p.h
+++ b/logic/BaseInstance_p.h
@@ -14,10 +14,13 @@
*/
#pragma once
+
#include <QString>
+#include <QSet>
+
#include <settingsobject.h>
-class BaseInstance;
+#include "BaseInstance.h"
#define I_D(Class) Class##Private *const d = (Class##Private * const)inst_d.get()
@@ -26,5 +29,5 @@ struct BaseInstancePrivate
QString m_rootDir;
QString m_group;
SettingsObject *m_settings;
- int m_flags;
+ QSet<BaseInstance::InstanceFlag> m_flags;
};
diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp
index 6f238c21..48bfb8a3 100644
--- a/logic/ForgeInstaller.cpp
+++ b/logic/ForgeInstaller.cpp
@@ -24,17 +24,24 @@
#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(QString filename, QString universal_url)
+ForgeInstaller::ForgeInstaller()
+ : BaseInstaller()
+{
+}
+void ForgeInstaller::prepare(const QString &filename, const QString &universalUrl)
{
std::shared_ptr<VersionFinal> newVersion;
- m_universal_url = universal_url;
+ m_universal_url = universalUrl;
QuaZip zip(filename);
if (!zip.open(QuaZip::mdUnzip))
@@ -111,7 +118,6 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url)
m_forge_version = newVersion;
realVersionId = m_forge_version->id = installObj.value("minecraft").toString();
}
-
bool ForgeInstaller::add(OneSixInstance *to)
{
if (!BaseInstaller::add(to))
@@ -226,3 +232,96 @@ bool ForgeInstaller::add(OneSixInstance *to)
return true;
}
+
+class ForgeInstallTask : public Task
+{
+ Q_OBJECT
+public:
+ ForgeInstallTask(ForgeInstaller *installer, OneSixInstance *instance, BaseVersionPtr version, QObject *parent = 0)
+ : Task(parent), m_installer(installer), m_instance(instance), m_version(version)
+ {
+ }
+
+protected:
+ void executeTask() override
+ {
+ {
+ 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)
+ {
+ 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();
+ }
+ });
+ }
+ else
+ {
+ if (!install(entry, forgeVersion))
+ {
+ QLOG_ERROR() << "Failure installing forge";
+ emitFailed(tr("Failure to install forge"));
+ }
+ else
+ {
+ reload();
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ void reload()
+ {
+ try
+ {
+ m_instance->reloadVersion();
+ emitSucceeded();
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(e.cause());
+ }
+ catch (...)
+ {
+ emitFailed(tr("Failed to load the version description file for reasons unknown."));
+ }
+ }
+
+private:
+ ForgeInstaller *m_installer;
+ OneSixInstance *m_instance;
+ BaseVersionPtr m_version;
+};
+
+ProgressProvider *ForgeInstaller::createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent)
+{
+ return new ForgeInstallTask(this, instance, version, parent);
+}
+
+#include "ForgeInstaller.moc"
diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h
index df029f38..05cc994b 100644
--- a/logic/ForgeInstaller.h
+++ b/logic/ForgeInstaller.h
@@ -25,12 +25,15 @@ class VersionFinal;
class ForgeInstaller : public BaseInstaller
{
public:
- ForgeInstaller(QString filename, QString universal_url);
+ ForgeInstaller();
+ 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;
+
private:
// the version, read from the installer
std::shared_ptr<VersionFinal> m_forge_version;
diff --git a/logic/LegacyFTBInstance.cpp b/logic/LegacyFTBInstance.cpp
index 23cb259d..3c3356c9 100644
--- a/logic/LegacyFTBInstance.cpp
+++ b/logic/LegacyFTBInstance.cpp
@@ -7,7 +7,7 @@ LegacyFTBInstance::LegacyFTBInstance(const QString &rootDir, SettingsObject *set
QString LegacyFTBInstance::getStatusbarDescription()
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return "Legacy FTB: " + intendedVersionId() + " (broken)";
}
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index 3b9181e0..4f2dfd9b 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -268,7 +268,7 @@ QString LegacyInstance::defaultCustomBaseJar() const
bool LegacyInstance::menuActionEnabled(QString action_name) const
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return false;
}
@@ -281,7 +281,7 @@ bool LegacyInstance::menuActionEnabled(QString action_name) const
QString LegacyInstance::getStatusbarDescription()
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return "Legacy : " + intendedVersionId() + " (broken)";
}
diff --git a/logic/LiteLoaderInstaller.cpp b/logic/LiteLoaderInstaller.cpp
index bb4b07ca..99cc5643 100644
--- a/logic/LiteLoaderInstaller.cpp
+++ b/logic/LiteLoaderInstaller.cpp
@@ -23,12 +23,17 @@
#include "VersionFinal.h"
#include "OneSixLibrary.h"
#include "OneSixInstance.h"
+#include "MultiMC.h"
+#include "lists/LiteLoaderVersionList.h"
-LiteLoaderInstaller::LiteLoaderInstaller(LiteLoaderVersionPtr version)
- : BaseInstaller(), m_version(version)
+LiteLoaderInstaller::LiteLoaderInstaller() : BaseInstaller()
{
}
+void LiteLoaderInstaller::prepare(LiteLoaderVersionPtr version)
+{
+ m_version = version;
+}
bool LiteLoaderInstaller::add(OneSixInstance *to)
{
if (!BaseInstaller::add(to))
@@ -84,3 +89,62 @@ bool LiteLoaderInstaller::add(OneSixInstance *to)
return true;
}
+
+class LiteLoaderInstallTask : public Task
+{
+ Q_OBJECT
+public:
+ LiteLoaderInstallTask(LiteLoaderInstaller *installer, OneSixInstance *instance,
+ BaseVersionPtr version, QObject *parent)
+ : Task(parent), m_installer(installer), m_instance(instance), m_version(version)
+ {
+ }
+
+protected:
+ void executeTask() override
+ {
+ LiteLoaderVersionPtr liteloaderVersion =
+ std::dynamic_pointer_cast<LiteLoaderVersion>(m_version);
+ if (!liteloaderVersion)
+ {
+ return;
+ }
+ m_installer->prepare(liteloaderVersion);
+ if (!m_installer->add(m_instance))
+ {
+ emitFailed(tr("For reasons unknown, the LiteLoader installation failed. Check your "
+ "MultiMC log files for details."));
+ }
+ else
+ {
+ try
+ {
+ m_instance->reloadVersion();
+ emitSucceeded();
+ }
+ catch (MMCError &e)
+ {
+ emitFailed(e.cause());
+ }
+ catch (...)
+ {
+ emitFailed(
+ tr("Failed to load the version description file for reasons unknown."));
+ }
+ }
+ }
+
+private:
+ LiteLoaderInstaller *m_installer;
+ OneSixInstance *m_instance;
+ BaseVersionPtr m_version;
+};
+
+ProgressProvider *LiteLoaderInstaller::createInstallTask(OneSixInstance *instance,
+ BaseVersionPtr version,
+ QObject *parent)
+{
+ return new LiteLoaderInstallTask(this, instance, version, parent);
+}
+
+#include "LiteLoaderInstaller.moc"
diff --git a/logic/LiteLoaderInstaller.h b/logic/LiteLoaderInstaller.h
index 2e0de64a..3ab5acb2 100644
--- a/logic/LiteLoaderInstaller.h
+++ b/logic/LiteLoaderInstaller.h
@@ -25,10 +25,13 @@
class LiteLoaderInstaller : public BaseInstaller
{
public:
- LiteLoaderInstaller(LiteLoaderVersionPtr version);
+ LiteLoaderInstaller();
+ void prepare(LiteLoaderVersionPtr version);
bool add(OneSixInstance *to) override;
+ ProgressProvider *createInstallTask(OneSixInstance *instance, BaseVersionPtr version, QObject *parent) override;
+
private:
virtual QString id() const override
{
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index effa1095..2dd40e99 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -259,7 +259,14 @@ void MinecraftProcess::finish(int code, ExitStatus status)
void MinecraftProcess::killMinecraft()
{
killed = true;
- kill();
+ if (m_prepostlaunchprocess.state() == QProcess::Running)
+ {
+ m_prepostlaunchprocess.kill();
+ }
+ else
+ {
+ kill();
+ }
}
bool MinecraftProcess::preLaunch()
@@ -271,8 +278,10 @@ bool MinecraftProcess::preLaunch()
// Launch
emit log(tr("Running Pre-Launch command: %1").arg(prelaunch_cmd));
m_prepostlaunchprocess.start(prelaunch_cmd);
- // Wait
- m_prepostlaunchprocess.waitForFinished();
+ if (!waitForPrePost())
+ {
+ return false;
+ }
// Flush console window
if (!m_err_leftover.isEmpty())
{
@@ -310,7 +319,10 @@ bool MinecraftProcess::postLaunch()
postlaunch_cmd = substituteVariables(postlaunch_cmd);
emit log(tr("Running Post-Launch command: %1").arg(postlaunch_cmd));
m_prepostlaunchprocess.start(postlaunch_cmd);
- m_prepostlaunchprocess.waitForFinished();
+ if (!waitForPrePost())
+ {
+ return false;
+ }
// Flush console window
if (!m_err_leftover.isEmpty())
{
@@ -338,6 +350,23 @@ bool MinecraftProcess::postLaunch()
return true;
}
+bool MinecraftProcess::waitForPrePost()
+{
+ m_prepostlaunchprocess.waitForStarted();
+ QEventLoop eventLoop;
+ auto finisher = [this, &eventLoop](QProcess::ProcessState state)
+ {
+ if (state == QProcess::NotRunning)
+ {
+ eventLoop.quit();
+ }
+ };
+ auto connection = connect(&m_prepostlaunchprocess, &QProcess::stateChanged, finisher);
+ int ret = eventLoop.exec();
+ disconnect(connection);
+ return ret == 0;
+}
+
QMap<QString, QString> MinecraftProcess::getVariables() const
{
QMap<QString, QString> out;
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
index d91dad56..d47a6cd2 100644
--- a/logic/MinecraftProcess.h
+++ b/logic/MinecraftProcess.h
@@ -133,6 +133,7 @@ protected:
bool preLaunch();
bool postLaunch();
+ bool waitForPrePost();
QMap<QString, QString> getVariables() const;
QString substituteVariables(const QString &cmd) const;
diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp
index 96ce4cc7..52820725 100644
--- a/logic/NostalgiaInstance.cpp
+++ b/logic/NostalgiaInstance.cpp
@@ -23,7 +23,7 @@ NostalgiaInstance::NostalgiaInstance(const QString &rootDir, SettingsObject *set
QString NostalgiaInstance::getStatusbarDescription()
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return "Nostalgia : " + intendedVersionId() + " (broken)";
}
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 8f70ed08..172830bb 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -119,7 +119,7 @@ bool OneSixFTBInstance::providesVersionFile() const
QString OneSixFTBInstance::getStatusbarDescription()
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return "OneSix FTB: " + intendedVersionId() + " (broken)";
}
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index ed7ad993..ccc25845 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -333,14 +333,14 @@ void OneSixInstance::reloadVersion()
{
d->version->reload(false, externalPatches());
d->vanillaVersion->reload(true, externalPatches());
- setFlags(flags() & ~VersionBrokenFlag);
+ d->m_flags.remove(VersionBrokenFlag);
emit versionReloaded();
}
catch(MMCError & error)
{
d->version->clear();
d->vanillaVersion->clear();
- setFlags(flags() | VersionBrokenFlag);
+ d->m_flags.insert(VersionBrokenFlag);
//TODO: rethrow to show some error message(s)?
emit versionReloaded();
throw;
@@ -379,7 +379,7 @@ QString OneSixInstance::defaultCustomBaseJar() const
bool OneSixInstance::menuActionEnabled(QString action_name) const
{
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
return false;
}
@@ -397,7 +397,7 @@ QString OneSixInstance::getStatusbarDescription()
{
descr += " (custom)";
}
- if (flags() & VersionBrokenFlag)
+ if (flags().contains(VersionBrokenFlag))
{
descr += " (broken)";
}
diff --git a/logic/OneSixVersionBuilder.cpp b/logic/OneSixVersionBuilder.cpp
index 8eacbce4..35d01a46 100644
--- a/logic/OneSixVersionBuilder.cpp
+++ b/logic/OneSixVersionBuilder.cpp
@@ -58,13 +58,15 @@ void OneSixVersionBuilder::readJsonAndApplyToVersion(VersionFinal *version,
void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringList &external)
{
- m_version->clear();
+ 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;
@@ -72,11 +74,12 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
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();
- file->applyTo(m_version);
m_version->versionFiles.append(file);
}
+ }
// else, if there's custom json, we just do that.
else if (QFile::exists(root.absoluteFilePath("custom.json")))
{
@@ -85,8 +88,8 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
file->name = "custom.json";
file->filename = "custom.json";
file->fileId = "org.multimc.custom.json";
+ file->order = -1;
file->version = QString();
- file->applyTo(m_version);
m_version->versionFiles.append(file);
// QObject::tr("The version descriptors of this instance are not compatible with the
// current version of MultiMC"));
@@ -101,9 +104,9 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
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();
- file->applyTo(m_version);
m_version->versionFiles.append(file);
// QObject::tr("Error while applying %1. Please check MultiMC-0.log for more
// info.").arg(root.absoluteFilePath("version.json")));
@@ -128,9 +131,7 @@ void OneSixVersionBuilder::buildInternal(const bool onlyVanilla, const QStringLi
}
for (auto order : files.keys())
{
- QLOG_DEBUG() << "Applying file with order" << order;
auto &filePair = files[order];
- filePair.second->applyTo(m_version);
m_version->versionFiles.append(filePair.second);
}
} while (0);
diff --git a/logic/VersionFinal.cpp b/logic/VersionFinal.cpp
index a057ecdd..d0e85c15 100644
--- a/logic/VersionFinal.cpp
+++ b/logic/VersionFinal.cpp
@@ -17,8 +17,20 @@
#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)
@@ -31,12 +43,12 @@ bool 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()
{
- beginResetModel();
id.clear();
time.clear();
releaseTime.clear();
@@ -48,8 +60,6 @@ void VersionFinal::clear()
mainClass.clear();
libraries.clear();
tweakers.clear();
- versionFiles.clear();
- endResetModel();
}
bool VersionFinal::canRemove(const int index) const
@@ -60,6 +70,18 @@ bool VersionFinal::canRemove(const int index) const
}
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;
+}
QString VersionFinal::versionFileId(const int index) const
{
@@ -69,14 +91,16 @@ QString VersionFinal::versionFileId(const int index) const
}
return versionFiles.at(index)->fileId;
}
-
-bool VersionFinal::remove(const int index)
+VersionFilePtr VersionFinal::versionFile(const QString &id)
{
- if (canRemove(index))
+ for (auto file : versionFiles)
{
- return QFile::remove(versionFiles.at(index)->filename);
+ if (file->fileId == id)
+ {
+ return file;
+ }
}
- return false;
+ return 0;
}
QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
@@ -91,7 +115,6 @@ QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNormalLibs()
}
return output;
}
-
QList<std::shared_ptr<OneSixLibrary> > VersionFinal::getActiveNativeLibs()
{
QList<std::shared_ptr<OneSixLibrary> > output;
@@ -144,7 +167,6 @@ QVariant VersionFinal::data(const QModelIndex &index, int role) const
}
return QVariant();
}
-
QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal)
@@ -164,7 +186,6 @@ QVariant VersionFinal::headerData(int section, Qt::Orientation orientation, int
}
return QVariant();
}
-
Qt::ItemFlags VersionFinal::flags(const QModelIndex &index) const
{
if (!index.isValid())
@@ -181,3 +202,117 @@ 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);
+
+ if (!alreadyReseting)
+ {
+ endResetModel();
+ }
+}
diff --git a/logic/VersionFinal.h b/logic/VersionFinal.h
index 99fd5ff0..fcffb3c3 100644
--- a/logic/VersionFinal.h
+++ b/logic/VersionFinal.h
@@ -41,12 +41,22 @@ public:
bool reload(const bool onlyVanilla = false, const QStringList &external = QStringList());
void clear();
- void dump() const;
-
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();
+
+ enum MoveDirection { MoveUp, MoveDown };
+ void move(const int index, const MoveDirection direction);
+ void resetOrder();
+
+ // clears and reapplies all version files
+ void reapply(const bool alreadyReseting = false);
+
public
slots:
bool remove(const int index);
@@ -120,7 +130,9 @@ public:
// QList<Rule> rules;
QList<VersionFilePtr> versionFiles;
+ VersionFilePtr versionFile(const QString &id);
private:
OneSixInstance *m_instance;
+ QMap<QString, int> getExistingOrder() const;
};
diff --git a/resources/instances/broken.png b/resources/instances/broken.png
new file mode 100644
index 00000000..7a80a76e
--- /dev/null
+++ b/resources/instances/broken.png
Binary files differ
diff --git a/resources/instances/instances.qrc b/resources/instances/instances.qrc
index ec3cdf21..5124b846 100644
--- a/resources/instances/instances.qrc
+++ b/resources/instances/instances.qrc
@@ -32,4 +32,15 @@
<file alias="squarecreeper">squarecreeper128.png</file>
<file alias="steve">steve128.png</file>
</qresource>
+ <qresource prefix="/icons/badges">
+ <!-- Source: see above -->
+ <file>enderman.png</file>
+ <file>herobrine.png</file>
+ <file>derp.png</file>
+
+ <!-- Source: http://www.iconarchive.com/show/cat-icons-by-fasticon/Cat-Brown-icon.html -->
+ <file>kitten.png</file>
+ <!-- Source: http://www.iconarchive.com/show/crystal-clear-icons-by-everaldo/Filesystem-file-broken-icon.html -->
+ <file>broken.png</file>
+ </qresource>
</RCC>
diff --git a/resources/instances/kitten.png b/resources/instances/kitten.png
new file mode 100644
index 00000000..d17661ce
--- /dev/null
+++ b/resources/instances/kitten.png
Binary files differ