NOISSUE refactor Mod a bunch, get rid of dead code

This commit is contained in:
Petr Mrázek 2019-08-03 05:30:46 +02:00
parent 40c9af1a8b
commit 7d13e31198
16 changed files with 284 additions and 375 deletions

View File

@ -27,6 +27,183 @@
#include <FileSystem.h> #include <FileSystem.h>
#include <QDebug> #include <QDebug>
namespace {
// NEW format
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
// OLD format:
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
ModDetails ReadMCModInfo(QByteArray contents)
{
auto getInfoFromArray = [&](QJsonArray arr)->ModDetails
{
ModDetails details;
if (!arr.at(0).isObject()) {
return details;
}
auto firstObj = arr.at(0).toObject();
details.mod_id = firstObj.value("modid").toString();
auto name = firstObj.value("name").toString();
// NOTE: ignore stupid example mods copies where the author didn't even bother to change the name
if(name != "Example Mod") {
details.name = name;
}
details.version = firstObj.value("version").toString();
details.updateurl = firstObj.value("updateUrl").toString();
auto homeurl = firstObj.value("url").toString().trimmed();
if(!homeurl.isEmpty())
{
// fix up url.
if (!homeurl.startsWith("http://") && !homeurl.startsWith("https://") && !homeurl.startsWith("ftp://"))
{
homeurl.prepend("http://");
}
}
details.homeurl = homeurl;
details.description = firstObj.value("description").toString();
QJsonArray authors = firstObj.value("authorList").toArray();
if (authors.size() == 0) {
// FIXME: what is the format of this? is there any?
authors = firstObj.value("authors").toArray();
}
for (auto author: authors)
{
details.authors.append(author.toString());
}
details.credits = firstObj.value("credits").toString();
details.valid = true;
return details;
};
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
// this is the very old format that had just the array
if (jsonDoc.isArray())
{
return getInfoFromArray(jsonDoc.array());
}
else if (jsonDoc.isObject())
{
auto val = jsonDoc.object().value("modinfoversion");
if(val.isUndefined()) {
val = jsonDoc.object().value("modListVersion");
}
int version = val.toDouble();
if (version != 2)
{
qCritical() << "BAD stuff happened to mod json:";
qCritical() << contents;
return ModDetails();
}
auto arrVal = jsonDoc.object().value("modlist");
if(arrVal.isUndefined()) {
arrVal = jsonDoc.object().value("modList");
}
if (arrVal.isArray())
{
return getInfoFromArray(arrVal.toArray());
}
}
return ModDetails();
}
// https://fabricmc.net/wiki/documentation:fabric_mod_json
ModDetails ReadFabricModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
ModDetails details;
details.mod_id = object.value("id").toString();
details.version = object.value("version").toString();
details.name = object.contains("name") ? object.value("name").toString() : details.mod_id;
details.description = object.value("description").toString();
if (schemaVersion >= 1)
{
QJsonArray authors = object.value("authors").toArray();
for (auto author: authors)
{
if(author.isObject()) {
details.authors.append(author.toObject().value("name").toString());
}
else {
details.authors.append(author.toString());
}
}
if (object.contains("contact"))
{
QJsonObject contact = object.value("contact").toObject();
if (contact.contains("homepage"))
{
details.homeurl = contact.value("homepage").toString();
}
}
}
details.valid = !details.name.isEmpty();
return details;
}
ModDetails ReadForgeInfo(QByteArray contents)
{
ModDetails details;
// Read the data
details.name = "Minecraft Forge";
details.mod_id = "Forge";
details.homeurl = "http://www.minecraftforge.net/forum/";
details.valid = true;
INIFile ini;
if (!ini.loadFile(contents))
return details;
QString major = ini.get("forge.major.number", "0").toString();
QString minor = ini.get("forge.minor.number", "0").toString();
QString revision = ini.get("forge.revision.number", "0").toString();
QString build = ini.get("forge.build.number", "0").toString();
details.version = major + "." + minor + "." + revision + "." + build;
return details;
}
ModDetails ReadLiteModInfo(QByteArray contents)
{
ModDetails details;
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
if (object.contains("name"))
{
details.mod_id = details.name = object.value("name").toString();
}
if (object.contains("version"))
{
details.version = object.value("version").toString("");
}
else
{
details.version = object.value("revision").toString("");
}
details.mcversion = object.value("mcversion").toString();
auto author = object.value("author").toString();
if(!author.isEmpty()) {
details.authors.append(author);
}
details.description = object.value("description").toString();
details.homeurl = object.value("url").toString();
return details;
}
ModDetails invalidDetails;
}
Mod::Mod(const QFileInfo &file) Mod::Mod(const QFileInfo &file)
{ {
repath(file); repath(file);
@ -91,7 +268,7 @@ void Mod::repath(const QFileInfo &file)
return; return;
} }
ReadMCModInfo(file.readAll()); m_localDetails = ReadMCModInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return; return;
@ -104,7 +281,7 @@ void Mod::repath(const QFileInfo &file)
return; return;
} }
ReadFabricModInfo(file.readAll()); m_localDetails = ReadFabricModInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return; return;
@ -117,7 +294,7 @@ void Mod::repath(const QFileInfo &file)
return; return;
} }
ReadForgeInfo(file.readAll()); m_localDetails = ReadForgeInfo(file.readAll());
file.close(); file.close();
zip.close(); zip.close();
return; return;
@ -136,7 +313,7 @@ void Mod::repath(const QFileInfo &file)
auto data = mcmod.readAll(); auto data = mcmod.readAll();
if (data.isEmpty() || data.isNull()) if (data.isEmpty() || data.isNull())
return; return;
ReadMCModInfo(data); m_localDetails = ReadMCModInfo(data);
} }
} }
else if (m_type == MOD_LITEMOD) else if (m_type == MOD_LITEMOD)
@ -155,245 +332,13 @@ void Mod::repath(const QFileInfo &file)
return; return;
} }
ReadLiteModInfo(file.readAll()); m_localDetails = ReadLiteModInfo(file.readAll());
file.close(); file.close();
} }
zip.close(); zip.close();
} }
} }
// NEW format
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/6f62b37cea040daf350dc253eae6326dd9c822c3
// OLD format:
// https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc
void Mod::ReadMCModInfo(QByteArray contents)
{
auto getInfoFromArray = [&](QJsonArray arr)->void
{
if (!arr.at(0).isObject())
return;
auto firstObj = arr.at(0).toObject();
m_mod_id = firstObj.value("modid").toString();
m_name = firstObj.value("name").toString();
m_version = firstObj.value("version").toString();
m_homeurl = firstObj.value("url").toString();
m_updateurl = firstObj.value("updateUrl").toString();
m_homeurl = m_homeurl.trimmed();
if(!m_homeurl.isEmpty())
{
// fix up url.
if (!m_homeurl.startsWith("http://") && !m_homeurl.startsWith("https://") &&
!m_homeurl.startsWith("ftp://"))
{
m_homeurl.prepend("http://");
}
}
m_description = firstObj.value("description").toString();
QJsonArray authors = firstObj.value("authorList").toArray();
if (authors.size() == 0)
authors = firstObj.value("authors").toArray();
if (authors.size() == 0)
m_authors = "";
else if (authors.size() >= 1)
{
m_authors = authors.at(0).toString();
for (int i = 1; i < authors.size(); i++)
{
m_authors += ", " + authors.at(i).toString();
}
}
m_credits = firstObj.value("credits").toString();
return;
}
;
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
// this is the very old format that had just the array
if (jsonDoc.isArray())
{
getInfoFromArray(jsonDoc.array());
}
else if (jsonDoc.isObject())
{
auto val = jsonDoc.object().value("modinfoversion");
if(val.isUndefined())
val = jsonDoc.object().value("modListVersion");
int version = val.toDouble();
if (version != 2)
{
qCritical() << "BAD stuff happened to mod json:";
qCritical() << contents;
return;
}
auto arrVal = jsonDoc.object().value("modlist");
if(arrVal.isUndefined())
arrVal = jsonDoc.object().value("modList");
if (arrVal.isArray())
{
getInfoFromArray(arrVal.toArray());
}
}
}
// https://fabricmc.net/wiki/documentation:fabric_mod_json
void Mod::ReadFabricModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
m_mod_id = object.value("id").toString();
m_version = object.value("version").toString();
m_name = object.contains("name") ? object.value("name").toString() : m_mod_id;
m_description = object.value("description").toString();
if (schemaVersion >= 1)
{
QJsonArray authors = object.value("authors").toArray();
m_authors = "";
for (int i = 0; i < authors.size(); i++)
{
QString author_name = authors.at(i).isObject()
? authors.at(i).toObject().value("name").toString()
: authors.at(i).toString();
if (i > 0)
m_authors += ", " + author_name;
else {
m_authors += author_name;
}
}
if (object.contains("contact"))
{
QJsonObject contact = object.value("contact").toObject();
if (contact.contains("homepage"))
m_homeurl = contact.value("homepage").toString();
}
}
}
void Mod::ReadForgeInfo(QByteArray contents)
{
// Read the data
m_name = "Minecraft Forge";
m_mod_id = "Forge";
m_homeurl = "http://www.minecraftforge.net/forum/";
INIFile ini;
if (!ini.loadFile(contents))
return;
QString major = ini.get("forge.major.number", "0").toString();
QString minor = ini.get("forge.minor.number", "0").toString();
QString revision = ini.get("forge.revision.number", "0").toString();
QString build = ini.get("forge.build.number", "0").toString();
m_version = major + "." + minor + "." + revision + "." + build;
}
void Mod::ReadLiteModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
if (object.contains("name"))
{
m_mod_id = m_name = object.value("name").toString();
}
if (object.contains("version"))
{
m_version = object.value("version").toString("");
}
else
{
m_version = object.value("revision").toString("");
}
m_mcversion = object.value("mcversion").toString();
m_authors = object.value("author").toString();
m_description = object.value("description").toString();
m_homeurl = object.value("url").toString();
}
bool Mod::replace(Mod &with)
{
if (!destroy())
return false;
bool success = false;
auto t = with.type();
if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE || t == MOD_LITEMOD)
{
qDebug() << "Copy: " << with.m_file.filePath() << " to " << m_file.filePath();
success = QFile::copy(with.m_file.filePath(), m_file.filePath());
}
if (t == MOD_FOLDER)
{
success = FS::copy(with.m_file.filePath(), m_file.path())();
}
if (success)
{
m_name = with.m_name;
m_mmc_id = with.m_mmc_id;
m_mod_id = with.m_mod_id;
m_version = with.m_version;
m_mcversion = with.m_mcversion;
m_description = with.m_description;
m_authors = with.m_authors;
m_credits = with.m_credits;
m_homeurl = with.m_homeurl;
m_type = with.m_type;
m_file.refresh();
}
return success;
}
bool Mod::destroy()
{
if (m_type == MOD_FOLDER)
{
QDir d(m_file.filePath());
if (d.removeRecursively())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD)
{
QFile f(m_file.filePath());
if (f.remove())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
return true;
}
QString Mod::version() const
{
switch (type())
{
case MOD_ZIPFILE:
case MOD_LITEMOD:
return m_version;
case MOD_FOLDER:
return "Folder";
case MOD_SINGLEFILE:
return "File";
default:
return "VOID";
}
}
bool Mod::enable(bool value) bool Mod::enable(bool value)
{ {
if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER)
@ -423,11 +368,66 @@ bool Mod::enable(bool value)
m_enabled = value; m_enabled = value;
return true; return true;
} }
bool Mod::operator==(const Mod &other) const
bool Mod::destroy()
{ {
return mmc_id() == other.mmc_id(); if (m_type == MOD_FOLDER)
}
bool Mod::strongCompare(const Mod &other) const
{ {
return mmc_id() == other.mmc_id() && version() == other.version() && type() == other.type(); QDir d(m_file.filePath());
if (d.removeRecursively())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE || m_type == MOD_LITEMOD)
{
QFile f(m_file.filePath());
if (f.remove())
{
m_type = MOD_UNKNOWN;
return true;
}
return false;
}
return true;
}
const ModDetails & Mod::details() const
{
if(!m_localDetails)
return invalidDetails;
return m_localDetails;
}
QString Mod::version() const
{
return details().version;
}
QString Mod::name() const
{
auto & d = details();
if(d && !d.name.isEmpty()) {
return d.name;
}
return m_name;
}
QString Mod::homeurl() const
{
return details().homeurl;
}
QString Mod::description() const
{
return details().description;
}
QStringList Mod::authors() const
{
return details().authors;
} }

View File

@ -16,8 +16,26 @@
#pragma once #pragma once
#include <QFileInfo> #include <QFileInfo>
#include <QDateTime> #include <QDateTime>
#include "multimc_logic_export.h"
class Mod struct ModDetails
{
operator bool() const {
return valid;
}
bool valid = false;
QString mod_id;
QString name;
QString version;
QString mcversion;
QString homeurl;
QString updateurl;
QString description;
QStringList authors;
QString credits;
};
class MULTIMC_LOGIC_EXPORT Mod
{ {
public: public:
enum ModType enum ModType
@ -39,10 +57,6 @@ public:
{ {
return m_mmc_id; return m_mmc_id;
} }
QString mod_id() const
{
return m_mod_id;
}
ModType type() const ModType type() const
{ {
return m_type; return m_type;
@ -51,37 +65,6 @@ public:
{ {
return m_type != MOD_UNKNOWN; return m_type != MOD_UNKNOWN;
} }
QString name() const
{
QString name = m_name.trimmed();
if(name.isEmpty() || name == "Example Mod")
{
return m_mmc_id;
}
return m_name;
}
QString version() const;
QString homeurl() const
{
return m_homeurl;
}
QString description() const
{
return m_description;
}
QString authors() const
{
return m_authors;
}
QString credits() const
{
return m_credits;
}
QDateTime dateTimeChanged() const QDateTime dateTimeChanged() const
{ {
@ -93,39 +76,29 @@ public:
return m_enabled; return m_enabled;
} }
const ModDetails &details() const;
QString name() const;
QString version() const;
QString homeurl() const;
QString description() const;
QStringList authors() const;
bool enable(bool value); bool enable(bool value);
// delete all the files of this mod // delete all the files of this mod
bool destroy(); bool destroy();
// replace this mod with a copy of the other
bool replace(Mod &with);
// change the mod's filesystem path (used by mod lists for *MAGIC* purposes) // change the mod's filesystem path (used by mod lists for *MAGIC* purposes)
void repath(const QFileInfo &file); void repath(const QFileInfo &file);
// WEAK compare operator - used for replacing mods
bool operator==(const Mod &other) const;
bool strongCompare(const Mod &other) const;
private:
void ReadMCModInfo(QByteArray contents);
void ReadFabricModInfo(QByteArray contents);
void ReadForgeInfo(QByteArray contents);
void ReadLiteModInfo(QByteArray contents);
protected: protected:
QFileInfo m_file; QFileInfo m_file;
QDateTime m_changedDateTime; QDateTime m_changedDateTime;
QString m_mmc_id; QString m_mmc_id;
QString m_mod_id;
bool m_enabled = true;
QString m_name; QString m_name;
QString m_version; bool m_enabled = true;
QString m_mcversion; ModType m_type = MOD_UNKNOWN;
QString m_homeurl; bool m_bare = true;
QString m_updateurl; ModDetails m_localDetails;
QString m_description;
QString m_authors;
QString m_credits;
ModType m_type;
}; };

View File

@ -249,8 +249,17 @@ QVariant SimpleModList::data(const QModelIndex &index, int role) const
{ {
case NameColumn: case NameColumn:
return mods[row].name(); return mods[row].name();
case VersionColumn: case VersionColumn: {
switch(mods[row].type()) {
case Mod::MOD_FOLDER:
return tr("Folder");
case Mod::MOD_SINGLEFILE:
return tr("File");
default:
break;
}
return mods[row].version(); return mods[row].version();
}
case DateColumn: case DateColumn:
return mods[row].dateTimeChanged(); return mods[row].dateTimeChanged();

View File

@ -429,7 +429,3 @@ bool World::operator==(const World &other) const
{ {
return is_valid == other.is_valid && folderName() == other.folderName(); return is_valid == other.is_valid && folderName() == other.folderName();
} }
bool World::strongCompare(const World &other) const
{
return is_valid == other.is_valid && folderName() == other.folderName();
}

View File

@ -76,7 +76,6 @@ public:
// WEAK compare operator - used for replacing worlds // WEAK compare operator - used for replacing worlds
bool operator==(const World &other) const; bool operator==(const World &other) const;
bool strongCompare(const World &other) const;
private: private:
void readFromZip(const QFileInfo &file); void readFromZip(const QFileInfo &file);

View File

@ -21,7 +21,6 @@
#include "LegacyInstance.h" #include "LegacyInstance.h"
#include "minecraft/legacy/LegacyModList.h" #include "minecraft/legacy/LegacyModList.h"
#include "minecraft/SimpleModList.h"
#include "minecraft/WorldList.h" #include "minecraft/WorldList.h"
#include <MMCZip.h> #include <MMCZip.h>
#include <FileSystem.h> #include <FileSystem.h>

View File

@ -19,17 +19,8 @@
#include <QString> #include <QString>
#include <QDir> #include <QDir>
#include "minecraft/Mod.h"
#include "multimc_logic_export.h" #include "multimc_logic_export.h"
class LegacyInstance;
class BaseInstance;
/**
* A legacy mod list.
* Backed by a folder.
*/
class MULTIMC_LOGIC_EXPORT LegacyModList class MULTIMC_LOGIC_EXPORT LegacyModList
{ {
public: public:

View File

@ -159,8 +159,6 @@ SET(MULTIMC_SOURCES
dialogs/IconPickerDialog.h dialogs/IconPickerDialog.h
dialogs/LoginDialog.cpp dialogs/LoginDialog.cpp
dialogs/LoginDialog.h dialogs/LoginDialog.h
dialogs/ModEditDialogCommon.cpp
dialogs/ModEditDialogCommon.h
dialogs/NewComponentDialog.cpp dialogs/NewComponentDialog.cpp
dialogs/NewComponentDialog.h dialogs/NewComponentDialog.h
dialogs/NewInstanceDialog.cpp dialogs/NewInstanceDialog.cpp

View File

@ -1,40 +0,0 @@
#include "ModEditDialogCommon.h"
#include "CustomMessageBox.h"
#include <QUrl>
bool lastfirst(QModelIndexList &list, int &first, int &last)
{
if (list.isEmpty())
return false;
first = last = list[0].row();
for (auto item : list)
{
int row = item.row();
if (row < first)
first = row;
if (row > last)
last = row;
}
return true;
}
void showWebsiteForMod(QWidget *parentDlg, Mod &m)
{
QString url = m.homeurl();
if (url.size())
{
// catch the cases where the protocol is missing
if (!url.startsWith("http"))
{
url = "http://" + url;
}
DesktopServices::openUrl(url);
}
else
{
CustomMessageBox::selectable(
parentDlg, QObject::tr("How sad!"),
QObject::tr("The mod author didn't provide a website link for this mod."),
QMessageBox::Warning);
}
}

View File

@ -1,9 +0,0 @@
#pragma once
#include <QModelIndex>
#include <DesktopServices.h>
#include <QWidget>
#include <minecraft/Mod.h>
bool lastfirst(QModelIndexList &list, int &first, int &last);
void showWebsiteForMod(QWidget *parentDlg, Mod &m);

View File

@ -24,7 +24,6 @@
#include "MultiMC.h" #include "MultiMC.h"
#include "dialogs/CustomMessageBox.h" #include "dialogs/CustomMessageBox.h"
#include "dialogs/ModEditDialogCommon.h"
#include <GuiUtil.h> #include <GuiUtil.h>
#include "minecraft/SimpleModList.h" #include "minecraft/SimpleModList.h"
#include "minecraft/Mod.h" #include "minecraft/Mod.h"

View File

@ -27,7 +27,6 @@
#include "dialogs/CustomMessageBox.h" #include "dialogs/CustomMessageBox.h"
#include "dialogs/VersionSelectDialog.h" #include "dialogs/VersionSelectDialog.h"
#include "dialogs/NewComponentDialog.h" #include "dialogs/NewComponentDialog.h"
#include "dialogs/ModEditDialogCommon.h"
#include "dialogs/ProgressDialog.h" #include "dialogs/ProgressDialog.h"
#include <GuiUtil.h> #include <GuiUtil.h>

View File

@ -17,7 +17,6 @@
#include "ui_WorldListPage.h" #include "ui_WorldListPage.h"
#include "minecraft/WorldList.h" #include "minecraft/WorldList.h"
#include <DesktopServices.h> #include <DesktopServices.h>
#include "dialogs/ModEditDialogCommon.h"
#include <QEvent> #include <QEvent>
#include <QMenu> #include <QMenu>
#include <QKeyEvent> #include <QKeyEvent>

View File

@ -40,7 +40,7 @@ void MCModInfoFrame::updateWithMod(Mod &m)
else else
text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>"; text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
if (!m.authors().isEmpty()) if (!m.authors().isEmpty())
text += " by " + m.authors(); text += " by " + m.authors().join(", ");
setModText(text); setModText(text);

View File

@ -16,8 +16,6 @@
#pragma once #pragma once
#include <QTreeView> #include <QTreeView>
class Mod;
class ModListView: public QTreeView class ModListView: public QTreeView
{ {
Q_OBJECT Q_OBJECT

View File

@ -16,8 +16,6 @@
#pragma once #pragma once
#include <QTreeView> #include <QTreeView>
class Mod;
class VersionListView : public QTreeView class VersionListView : public QTreeView
{ {
Q_OBJECT Q_OBJECT