summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2016-02-21 01:44:27 +0100
committerPetr Mrázek <peterix@gmail.com>2016-02-21 01:44:27 +0100
commit1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae (patch)
tree2c2fbc7a74ea5035fdfd8752272e011852eb1b85
parent495d320ce267cf6d7c7e4468dbc36d3536bcb401 (diff)
downloadMultiMC-1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae.tar
MultiMC-1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae.tar.gz
MultiMC-1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae.tar.lz
MultiMC-1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae.tar.xz
MultiMC-1a0bbdd9acaf1c5edaad6d3f0790c1b02674c0ae.zip
GH-1453 report version file problems in the version page
-rw-r--r--application/pages/VersionPage.cpp96
-rw-r--r--application/pages/VersionPage.h2
-rw-r--r--application/pages/VersionPage.ui24
-rw-r--r--application/widgets/ModListView.cpp4
-rw-r--r--logic/minecraft/MinecraftProfile.cpp26
-rw-r--r--logic/minecraft/MinecraftVersion.cpp54
-rw-r--r--logic/minecraft/MinecraftVersion.h7
-rw-r--r--logic/minecraft/OneSixInstance.cpp19
-rw-r--r--logic/minecraft/ProfilePatch.h54
-rw-r--r--logic/minecraft/ProfileUtils.cpp19
-rw-r--r--logic/minecraft/VersionFile.cpp26
11 files changed, 305 insertions, 26 deletions
diff --git a/application/pages/VersionPage.cpp b/application/pages/VersionPage.cpp
index e1abcf0c..f01a2d62 100644
--- a/application/pages/VersionPage.cpp
+++ b/application/pages/VersionPage.cpp
@@ -47,6 +47,51 @@
#include "icons/IconList.h"
#include "Exception.h"
+#include "MultiMC.h"
+
+class IconProxy : public QIdentityProxyModel
+{
+ Q_OBJECT
+public:
+
+ IconProxy(QWidget *parentWidget) : QIdentityProxyModel(parentWidget)
+ {
+ connect(parentWidget, &QObject::destroyed, this, &IconProxy::widgetGone);
+ m_parentWidget = parentWidget;
+ }
+
+ virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override
+ {
+ QVariant var = QIdentityProxyModel::data(mapToSource(proxyIndex), role);
+ int column = proxyIndex.column();
+ if(column == 0 && role == Qt::DecorationRole && m_parentWidget)
+ {
+ if(!var.isNull())
+ {
+ auto string = var.toString();
+ if(string == "warning")
+ {
+ return MMC->getThemedIcon("status-yellow");
+ }
+ else if(string == "error")
+ {
+ return MMC->getThemedIcon("status-bad");
+ }
+ }
+ return MMC->getThemedIcon("status-good");
+ }
+ return var;
+ }
+private slots:
+ void widgetGone()
+ {
+ m_parentWidget = nullptr;
+ }
+
+private:
+ QWidget *m_parentWidget = nullptr;
+};
+
QIcon VersionPage::icon() const
{
return ENV.icons()->getIcon(m_inst->iconKey());
@@ -72,11 +117,15 @@ VersionPage::VersionPage(OneSixInstance *inst, QWidget *parent)
m_version = m_inst->getMinecraftProfile();
if (m_version)
{
- ui->packageView->setModel(m_version.get());
+ auto proxy = new IconProxy(ui->packageView);
+ proxy->setSourceModel(m_version.get());
+ ui->packageView->setModel(proxy);
ui->packageView->installEventFilter(this);
ui->packageView->setSelectionMode(QAbstractItemView::SingleSelection);
connect(ui->packageView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &VersionPage::versionCurrent);
+ auto smodel = ui->packageView->selectionModel();
+ connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), SLOT(packageCurrent(QModelIndex, QModelIndex)));
updateVersionControls();
// select first item.
preselect(0);
@@ -94,6 +143,49 @@ VersionPage::~VersionPage()
delete ui;
}
+void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &previous)
+{
+ if (!current.isValid())
+ {
+ ui->frame->clear();
+ return;
+ }
+ int row = current.row();
+ auto patch = m_version->versionPatch(row);
+ auto severity = patch->getProblemSeverity();
+ switch(severity)
+ {
+ case PROBLEM_WARNING:
+ ui->frame->setModText(tr("%1 possibly has issues.").arg(patch->getPatchName()));
+ break;
+ case PROBLEM_ERROR:
+ ui->frame->setModText(tr("%1 has issues!").arg(patch->getPatchName()));
+ break;
+ default:
+ case PROBLEM_NONE:
+ ui->frame->clear();
+ return;
+ }
+
+ auto &problems = patch->getProblems();
+ QString problemOut;
+ for (auto &problem: problems)
+ {
+ if(problem.getSeverity() == PROBLEM_ERROR)
+ {
+ problemOut += tr("Error: ");
+ }
+ else if(problem.getSeverity() == PROBLEM_WARNING)
+ {
+ problemOut += tr("Warning: ");
+ }
+ problemOut += problem.getDescription();
+ problemOut += "\n";
+ }
+ ui->frame->setModDescription(problemOut);
+}
+
+
void VersionPage::updateVersionControls()
{
ui->forgeBtn->setEnabled(true);
@@ -453,3 +545,5 @@ void VersionPage::on_revertBtn_clicked()
updateButtons();
preselect(currentIdx);
}
+
+#include "VersionPage.moc"
diff --git a/application/pages/VersionPage.h b/application/pages/VersionPage.h
index 8f2bf024..216bc30e 100644
--- a/application/pages/VersionPage.h
+++ b/application/pages/VersionPage.h
@@ -90,4 +90,6 @@ public slots:
private slots:
void onGameUpdateError(QString error);
+ void packageCurrent(const QModelIndex &current, const QModelIndex &previous);
+
};
diff --git a/application/pages/VersionPage.ui b/application/pages/VersionPage.ui
index e88888c8..c16208db 100644
--- a/application/pages/VersionPage.ui
+++ b/application/pages/VersionPage.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>693</width>
- <height>575</height>
+ <height>750</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -32,8 +32,8 @@
<attribute name="title">
<string notr="true">Tab 1</string>
</attribute>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
<widget class="ModListView" name="packageView">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
@@ -49,7 +49,7 @@
</attribute>
</widget>
</item>
- <item>
+ <item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label">
@@ -245,6 +245,16 @@
</item>
</layout>
</item>
+ <item row="1" column="0" colspan="2">
+ <widget class="MCModInfoFrame" name="frame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</widget>
@@ -263,6 +273,12 @@
<header>widgets/LineSeparator.h</header>
<container>1</container>
</customwidget>
+ <customwidget>
+ <class>MCModInfoFrame</class>
+ <extends>QFrame</extends>
+ <header>widgets/MCModInfoFrame.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
diff --git a/application/widgets/ModListView.cpp b/application/widgets/ModListView.cpp
index aa16ad05..51b4a475 100644
--- a/application/widgets/ModListView.cpp
+++ b/application/widgets/ModListView.cpp
@@ -46,6 +46,10 @@ void ModListView::setModel ( QAbstractItemModel* model )
head->setStretchLastSection(false);
// HACK: this is true for the checkbox column of mod lists
auto string = model->headerData(0,head->orientation()).toString();
+ if(head->count() < 1)
+ {
+ return;
+ }
if(!string.size())
{
head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
diff --git a/logic/minecraft/MinecraftProfile.cpp b/logic/minecraft/MinecraftProfile.cpp
index 4f33920b..360fa575 100644
--- a/logic/minecraft/MinecraftProfile.cpp
+++ b/logic/minecraft/MinecraftProfile.cpp
@@ -295,6 +295,8 @@ QVariant MinecraftProfile::data(const QModelIndex &index, int role) const
if (row < 0 || row >= VersionPatches.size())
return QVariant();
+ auto patch = VersionPatches.at(row);
+
if (role == Qt::DisplayRole)
{
switch (column)
@@ -303,7 +305,6 @@ QVariant MinecraftProfile::data(const QModelIndex &index, int role) const
return VersionPatches.at(row)->getPatchName();
case 1:
{
- auto patch = VersionPatches.at(row);
if(patch->isCustom())
{
return QString("%1 (Custom)").arg(patch->getPatchVersion());
@@ -317,6 +318,29 @@ QVariant MinecraftProfile::data(const QModelIndex &index, int role) const
return QVariant();
}
}
+ if(role == Qt::DecorationRole)
+ {
+ switch(column)
+ {
+ case 0:
+ {
+ auto severity = patch->getProblemSeverity();
+ switch (severity)
+ {
+ case PROBLEM_WARNING:
+ return "warning";
+ case PROBLEM_ERROR:
+ return "error";
+ default:
+ return QVariant();
+ }
+ }
+ default:
+ {
+ return QVariant();
+ }
+ }
+ }
return QVariant();
}
QVariant MinecraftProfile::headerData(int section, Qt::Orientation orientation, int role) const
diff --git a/logic/minecraft/MinecraftVersion.cpp b/logic/minecraft/MinecraftVersion.cpp
index 96d64d6d..e6895bf7 100644
--- a/logic/minecraft/MinecraftVersion.cpp
+++ b/logic/minecraft/MinecraftVersion.cpp
@@ -89,12 +89,36 @@ QString MinecraftVersion::getUrl() const
VersionFilePtr MinecraftVersion::getVersionFile()
{
QFileInfo versionFile(QString("versions/%1/%1.dat").arg(m_descriptor));
-
- auto loadedVersionFile = ProfileUtils::parseBinaryJsonFile(versionFile);
- loadedVersionFile->name = "Minecraft";
- //FIXME: possibly not the best place for this... but w/e
- loadedVersionFile->setCustomizable(true);
- return loadedVersionFile;
+ m_problems.clear();
+ m_problemSeverity = PROBLEM_NONE;
+ if(!versionFile.exists())
+ {
+ if(m_loadedVersionFile)
+ {
+ m_loadedVersionFile.reset();
+ }
+ addProblem(PROBLEM_WARNING, QObject::tr("The patch file doesn't exist locally. It's possible it just needs to be downloaded."));
+ }
+ else
+ {
+ try
+ {
+ if(versionFile.lastModified() != m_loadedVersionFileTimestamp)
+ {
+ auto loadedVersionFile = ProfileUtils::parseBinaryJsonFile(versionFile);
+ loadedVersionFile->name = "Minecraft";
+ loadedVersionFile->setCustomizable(true);
+ m_loadedVersionFileTimestamp = versionFile.lastModified();
+ m_loadedVersionFile = loadedVersionFile;
+ }
+ }
+ catch(Exception e)
+ {
+ m_loadedVersionFile.reset();
+ addProblem(PROBLEM_ERROR, QObject::tr("The patch file couldn't be read:\n%1").arg(e.cause()));
+ }
+ }
+ return m_loadedVersionFile;
}
bool MinecraftVersion::isCustomizable()
@@ -114,6 +138,24 @@ bool MinecraftVersion::isCustomizable()
return false;
}
+const QList<PatchProblem> &MinecraftVersion::getProblems()
+{
+ if(m_versionSource != Builtin && getVersionFile())
+ {
+ return getVersionFile()->getProblems();
+ }
+ return ProfilePatch::getProblems();
+}
+
+ProblemSeverity MinecraftVersion::getProblemSeverity()
+{
+ if(m_versionSource != Builtin && getVersionFile())
+ {
+ return getVersionFile()->getProblemSeverity();
+ }
+ return ProfilePatch::getProblemSeverity();
+}
+
void MinecraftVersion::applyTo(MinecraftProfile *version)
{
// do we have this one cached?
diff --git a/logic/minecraft/MinecraftVersion.h b/logic/minecraft/MinecraftVersion.h
index 120ea8ce..afeac3b7 100644
--- a/logic/minecraft/MinecraftVersion.h
+++ b/logic/minecraft/MinecraftVersion.h
@@ -78,6 +78,9 @@ public: /* methods */
QString getUrl() const;
+ virtual const QList<PatchProblem> &getProblems() override;
+ virtual ProblemSeverity getProblemSeverity() override;
+
private: /* methods */
void applyFileTo(MinecraftProfile *version);
@@ -124,4 +127,8 @@ public: /* data */
/// an update available from Mojang
MinecraftVersionPtr upstreamUpdate;
+
+private: /* data */
+ QDateTime m_loadedVersionFileTimestamp;
+ mutable VersionFilePtr m_loadedVersionFile;
};
diff --git a/logic/minecraft/OneSixInstance.cpp b/logic/minecraft/OneSixInstance.cpp
index da1b21cb..9a3920cc 100644
--- a/logic/minecraft/OneSixInstance.cpp
+++ b/logic/minecraft/OneSixInstance.cpp
@@ -111,6 +111,14 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
// blatant self-promotion.
token_mapping["profile_name"] = token_mapping["version_name"] = "MultiMC5";
+ if(m_version->isVanilla())
+ {
+ token_mapping["version_type"] = m_version->type;
+ }
+ else
+ {
+ token_mapping["version_type"] = "custom";
+ }
QString absRootDir = QDir(minecraftRoot()).absolutePath();
token_mapping["game_directory"] = absRootDir;
@@ -119,10 +127,21 @@ QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
token_mapping["user_properties"] = session->serializeUserProperties();
token_mapping["user_type"] = session->user_type;
+
// 1.7.3+ assets tokens
token_mapping["assets_root"] = absAssetsDir;
token_mapping["assets_index_name"] = m_version->assets;
+ // 1.9+ version type token
+ if(m_version->isVanilla())
+ {
+ token_mapping["version_type"] = m_version->type;
+ }
+ else
+ {
+ token_mapping["version_type"] = "custom";
+ }
+
QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts);
for (int i = 0; i < parts.length(); i++)
{
diff --git a/logic/minecraft/ProfilePatch.h b/logic/minecraft/ProfilePatch.h
index de42cb7a..e4bb1c02 100644
--- a/logic/minecraft/ProfilePatch.h
+++ b/logic/minecraft/ProfilePatch.h
@@ -6,6 +6,35 @@
#include "JarMod.h"
class MinecraftProfile;
+
+enum ProblemSeverity
+{
+ PROBLEM_NONE,
+ PROBLEM_WARNING,
+ PROBLEM_ERROR
+};
+
+class PatchProblem
+{
+public:
+ PatchProblem(ProblemSeverity severity, const QString & description)
+ {
+ m_severity = severity;
+ m_description = description;
+ }
+ const QString & getDescription() const
+ {
+ return m_description;
+ }
+ const ProblemSeverity getSeverity() const
+ {
+ return m_severity;
+ }
+private:
+ ProblemSeverity m_severity;
+ QString m_description;
+};
+
class ProfilePatch
{
public:
@@ -32,6 +61,31 @@ public:
virtual QString getPatchName() = 0;
virtual QString getPatchVersion() = 0;
virtual QString getPatchFilename() = 0;
+
+ virtual const QList<PatchProblem>& getProblems()
+ {
+ return m_problems;
+ }
+ virtual void addProblem(ProblemSeverity severity, const QString &description)
+ {
+ if(severity > m_problemSeverity)
+ {
+ m_problemSeverity = severity;
+ }
+ m_problems.append(PatchProblem(severity, description));
+ }
+ virtual ProblemSeverity getProblemSeverity()
+ {
+ return m_problemSeverity;
+ }
+ virtual bool hasFailed()
+ {
+ return getProblemSeverity() == PROBLEM_ERROR;
+ }
+
+protected:
+ QList<PatchProblem> m_problems;
+ ProblemSeverity m_problemSeverity = PROBLEM_NONE;
};
typedef std::shared_ptr<ProfilePatch> ProfilePatchPtr;
diff --git a/logic/minecraft/ProfileUtils.cpp b/logic/minecraft/ProfileUtils.cpp
index 9a886f1d..5816f207 100644
--- a/logic/minecraft/ProfileUtils.cpp
+++ b/logic/minecraft/ProfileUtils.cpp
@@ -107,13 +107,26 @@ VersionFilePtr parseJsonFile(const QFileInfo &fileInfo, const bool requireOrder)
.arg(fileInfo.fileName(), file.errorString()));
}
QJsonParseError error;
- QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
+ auto data = file.readAll();
+ QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError)
{
+ int line = 0;
+ int column = 0;
+ for(int i = 0; i < error.offset; i++)
+ {
+ if(data[i] == '\n')
+ {
+ line++;
+ column = 0;
+ continue;
+ }
+ column++;
+ }
throw JSONValidationError(
- QObject::tr("Unable to process the version file %1: %2 at %3.")
+ QObject::tr("Unable to process the version file %1: %2 at line %3 column %4.")
.arg(fileInfo.fileName(), error.errorString())
- .arg(error.offset));
+ .arg(line).arg(column));
}
return VersionFile::fromJson(doc, file.fileName(), requireOrder);
}
diff --git a/logic/minecraft/VersionFile.cpp b/logic/minecraft/VersionFile.cpp
index 50f4c289..fee83900 100644
--- a/logic/minecraft/VersionFile.cpp
+++ b/logic/minecraft/VersionFile.cpp
@@ -15,8 +15,6 @@ using namespace Json;
#include "VersionBuildError.h"
#include <Version.h>
-#define CURRENT_MINIMUM_LAUNCHER_VERSION 18
-
static void readString(const QJsonObject &root, const QString &key, QString &variable)
{
if (root.contains(key))
@@ -50,6 +48,19 @@ int findLibraryByName(QList<OneSixLibraryPtr> haystack, const GradleSpecifier &n
return retval;
}
+void checkMinimumLauncherVersion(VersionFilePtr out)
+{
+ const int CURRENT_MINIMUM_LAUNCHER_VERSION = 14;
+ if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
+ {
+ out->addProblem(
+ PROBLEM_WARNING,
+ QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
+ .arg(out->minimumLauncherVersion)
+ .arg(CURRENT_MINIMUM_LAUNCHER_VERSION));
+ }
+}
+
VersionFilePtr VersionFile::fromMojangJson(const QJsonDocument &doc, const QString &filename)
{
VersionFilePtr out(new VersionFile());
@@ -82,6 +93,7 @@ VersionFilePtr VersionFile::fromMojangJson(const QJsonDocument &doc, const QStri
if (root.contains("minimumLauncherVersion"))
{
out->minimumLauncherVersion = requireInteger(root.value("minimumLauncherVersion"));
+ checkMinimumLauncherVersion(out);
}
if (root.contains("libraries"))
@@ -149,6 +161,7 @@ VersionFilePtr VersionFile::fromJson(const QJsonDocument &doc, const QString &fi
if (root.contains("minimumLauncherVersion"))
{
out->minimumLauncherVersion = requireInteger(root.value("minimumLauncherVersion"));
+ checkMinimumLauncherVersion(out);
}
if (root.contains("tweakers"))
@@ -304,15 +317,6 @@ bool VersionFile::hasJarMods()
void VersionFile::applyTo(MinecraftProfile *version)
{
- if (minimumLauncherVersion != -1)
- {
- if (minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
- {
- throw LauncherVersionError(minimumLauncherVersion,
- CURRENT_MINIMUM_LAUNCHER_VERSION);
- }
- }
-
if (!version->id.isNull() && !mcVersion.isNull())
{
if (QRegExp(mcVersion, Qt::CaseInsensitive, QRegExp::Wildcard).indexIn(version->id) ==