mirror of
https://github.com/ModOrganizer2/modorganizer-diagnose_basic.git
synced 2026-07-27 14:03:10 -07:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bce131613 | ||
|
|
849e946284 | ||
|
|
58a8406335 | ||
|
|
c8308f1612 | ||
|
|
75d821c46a |
+58
-62
@@ -23,6 +23,7 @@
|
||||
#include <QtPlugin>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QDateTime>
|
||||
@@ -54,6 +55,14 @@ bool DiagnoseBasic::init(IOrganizer *moInfo)
|
||||
m_MOInfo->modList()->onModStateChanged([&] (const QString &modName, IModList::ModStates) {
|
||||
if (modName == "Overwrite") invalidate();
|
||||
});
|
||||
m_MOInfo->modList()->onModMoved([&] (const QString&, int, int) {
|
||||
// invalidates only the assetOrder check but there is currently no way to recheck individual
|
||||
// checks
|
||||
invalidate();
|
||||
});
|
||||
m_MOInfo->pluginList()->onPluginMoved([&] (const QString&, int, int) {
|
||||
invalidate();
|
||||
});
|
||||
m_MOInfo->pluginList()->onRefreshed([&] () { this->invalidate(); });
|
||||
|
||||
return true;
|
||||
@@ -202,16 +211,15 @@ void DiagnoseBasic::topoSort(std::vector<DiagnoseBasic::ListElement> &list) cons
|
||||
typedef graph_traits<Graph>::vertex_descriptor Vertex;
|
||||
typedef std::list<Vertex> Order;
|
||||
|
||||
// figure out unconnected components of the graph.
|
||||
// figure out disconnected components of the graph
|
||||
std::vector<int> component(num_vertices(graph));
|
||||
connected_components(graph, &component[0]);
|
||||
|
||||
for (int i = 0; i != component.size(); ++i) {
|
||||
list[i].sortGroup = component[i];
|
||||
}
|
||||
|
||||
Order order;
|
||||
// do the actual sorting. This sorts the graph in full though the order between unconnected components doesn't
|
||||
// do the actual sorting. This sorts the graph in full although the order between unconnected components doesn't
|
||||
// really matter to us
|
||||
boost::topological_sort(graph, std::front_inserter(order));
|
||||
}
|
||||
@@ -224,22 +232,35 @@ void DiagnoseBasic::Sorter::sortGroup(std::vector<ListElement> modList)
|
||||
{
|
||||
auto maxSeqBegin = modList.end();
|
||||
auto maxSeqEnd = modList.end();
|
||||
int maxSeqAvoidMove = 0;
|
||||
|
||||
// first, determine the longest sequence of correctly sorted mods
|
||||
auto curSeqBegin = modList.begin();
|
||||
auto curSeqEnd = modList.begin();
|
||||
|
||||
int curSeqAvoidMove = 0;
|
||||
|
||||
auto iter = modList.begin() + 1;
|
||||
for (; iter != modList.end(); ++iter) {
|
||||
if (iter->modPriority < curSeqEnd->modPriority) {
|
||||
// sequence ends
|
||||
if ((maxSeqBegin == modList.end()) || ((curSeqEnd - curSeqBegin) > (maxSeqEnd - maxSeqBegin))) {
|
||||
// use this sequence of correctly sorted mods if it is longer than the previously longest
|
||||
// sequence and doesn't have fewer mods that don't want to move. Thus the need for mods to
|
||||
// stay in place beats our gole to have the minimal number of moves
|
||||
if ((maxSeqBegin == modList.end())
|
||||
|| (((curSeqEnd - curSeqBegin) > (maxSeqEnd - maxSeqBegin))
|
||||
&& (curSeqAvoidMove >= maxSeqAvoidMove))) {
|
||||
maxSeqBegin = curSeqBegin;
|
||||
maxSeqEnd = iter;
|
||||
maxSeqAvoidMove = curSeqAvoidMove;
|
||||
}
|
||||
curSeqBegin = curSeqEnd = iter;
|
||||
curSeqAvoidMove = 0;
|
||||
} else {
|
||||
curSeqEnd = iter;
|
||||
if (iter->avoidMove) {
|
||||
++curSeqAvoidMove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +335,9 @@ bool DiagnoseBasic::assetOrder() const
|
||||
}
|
||||
|
||||
// produce a list with the information we need: plugin, mod and the priority for each
|
||||
QStringList esps = m_MOInfo->findFiles("", [] (const QString &fileName) -> bool { return fileName.endsWith(".esp", Qt::CaseInsensitive); });
|
||||
QStringList esps = m_MOInfo->findFiles("",
|
||||
[] (const QString &fileName) -> bool { return fileName.endsWith(".esp", Qt::CaseInsensitive)
|
||||
|| fileName.endsWith(".esm", Qt::CaseInsensitive); });
|
||||
foreach (const QString &esp, esps) {
|
||||
ListElement ele;
|
||||
|
||||
@@ -323,9 +346,10 @@ bool DiagnoseBasic::assetOrder() const
|
||||
ele.pluginPriority = m_MOInfo->pluginList()->priority(ele.espName);
|
||||
ele.modPriority = m_MOInfo->modList()->priority(ele.modName);
|
||||
IModList::ModStates state = m_MOInfo->modList()->state(ele.modName);
|
||||
ele.avoidMove = state.testFlag(IModList::STATE_ESSENTIAL);
|
||||
auto iter = scriptMods.find(ele.modName);
|
||||
if (state.testFlag(IModList::STATE_EXISTS) && !state.testFlag(IModList::STATE_ESSENTIAL) &&
|
||||
(iter != scriptMods.end())) {
|
||||
if (state.testFlag(IModList::STATE_EXISTS)
|
||||
&& (iter != scriptMods.end())) {
|
||||
ele.relevantScripts = iter->second;
|
||||
modList.push_back(ele);
|
||||
}
|
||||
@@ -336,6 +360,10 @@ bool DiagnoseBasic::assetOrder() const
|
||||
// with esps from other mods
|
||||
std::vector<ListElement> distinctModList;
|
||||
{
|
||||
// sort the input list so we get predictable results
|
||||
std::sort(modList.begin(), modList.end(),
|
||||
[] (const ListElement &lhs, const ListElement &rhs) -> bool { return lhs.pluginPriority < rhs.pluginPriority; });
|
||||
|
||||
std::set<QString> includedMods;
|
||||
foreach(const ListElement &ele, modList) {
|
||||
if (includedMods.find(ele.modName) == includedMods.end()) {
|
||||
@@ -345,7 +373,7 @@ bool DiagnoseBasic::assetOrder() const
|
||||
}
|
||||
}
|
||||
|
||||
// sort the list by plugin priority
|
||||
// sort the list by plugin priority. This step is probably unnecessary as the list was already sorted when we removed duplicates
|
||||
std::sort(distinctModList.begin(), distinctModList.end(),
|
||||
[] (const ListElement &lhs, const ListElement &rhs) -> bool { return lhs.pluginPriority < rhs.pluginPriority; });
|
||||
|
||||
@@ -422,12 +450,6 @@ std::vector<unsigned int> DiagnoseBasic::activeProblems() const
|
||||
if (assetOrder()) {
|
||||
result.push_back(PROBLEM_ASSETORDER);
|
||||
}
|
||||
QStringList backups = QDir(m_MOInfo->profilePath()).entryList(QStringList() << "modlist.txt_backup_*");
|
||||
if (backups.size() > 0) {
|
||||
m_NewestModlistBackup = backups.last();
|
||||
result.push_back(PROBLEM_MODLISTBACKUP);
|
||||
}
|
||||
|
||||
if (QFile::exists(m_MOInfo->profilePath() + "/profile_tweaks.ini")) {
|
||||
result.push_back(PROBLEM_PROFILETWEAKS);
|
||||
}
|
||||
@@ -448,8 +470,6 @@ QString DiagnoseBasic::shortDescription(unsigned int key) const
|
||||
return tr("Nitpick installed");
|
||||
case PROBLEM_ASSETORDER:
|
||||
return tr("Potential Mod order problem");
|
||||
case PROBLEM_MODLISTBACKUP:
|
||||
return tr("Modlist backup exists");
|
||||
case PROBLEM_PROFILETWEAKS:
|
||||
return tr("Ini Tweaks overwritten");
|
||||
default:
|
||||
@@ -464,7 +484,7 @@ QString DiagnoseBasic::fullDescription(unsigned int key) const
|
||||
return "<code>" + m_ErrorMessage.replace("\n", "<br>") + "</code>";
|
||||
case PROBLEM_OVERWRITE:
|
||||
return tr("Files in the <font color=\"red\"><i>Overwrite</i></font> mod are are usually files created by an external tool (i.e. Wrye Bash, Automatic Variants, ...).<br>"
|
||||
"It is advisable you empty Overwrite directory by moving those files to an existing mod. You can do this by double-clicking the <font color=\"red\"><i>Overwrite</i></font> mod and use drag&drop to move the files to a mod.<br>"
|
||||
"It is advisable you empty the Overwrite directory by moving those files to an existing mod. You can do this by double-clicking the <font color=\"red\"><i>Overwrite</i></font> mod and use drag&drop to move the files to a mod.<br>"
|
||||
"Alternatively, right-click on <font color=\"red\"><i>Overwrite</i></font> and create a new regular mod from the files there.<br>"
|
||||
"<br>"
|
||||
"Why is this necessary? Generated files may depend on the other mods active in a profile and may thus be incompatible with a different profile (i.e. bashed patches from Wrye Bash). "
|
||||
@@ -479,9 +499,9 @@ QString DiagnoseBasic::fullDescription(unsigned int key) const
|
||||
case PROBLEM_ASSETORDER: {
|
||||
QString res = tr("The conflict resolution order for some mods containing scripts differs from that of the corresponding esp.<br>"
|
||||
"This may lead to subtle, hard to locate bugs. You should re-order the affected mods (left list!).<br>"
|
||||
"There is no way to reliably know if these changes are necessary but its definitively safer.<br>"
|
||||
"There is no way to reliably know if each of these changes is absolutely necessary but its definitively safer.<br>"
|
||||
"If someone suggested you ignore this message, please give them a proper slapping from me. <b>Do not ignore this warning</b><br>"
|
||||
"The following changes should fix the issue:") + "<ul>";
|
||||
"The following changes should prevent these kinds of errors:") + "<ul>";
|
||||
foreach(const Move &op, m_SuggestedMoves) {
|
||||
if (op.type == Move::BEFORE) {
|
||||
res += "<li>" + tr("Move %1 before %2").arg(op.item.modName).arg(op.reference.modName) + "</li>";
|
||||
@@ -492,14 +512,6 @@ QString DiagnoseBasic::fullDescription(unsigned int key) const
|
||||
res += "</ul>";
|
||||
return res;
|
||||
} break;
|
||||
case PROBLEM_MODLISTBACKUP: {
|
||||
uint timestamp = m_NewestModlistBackup.right(10).toULong();
|
||||
QDateTime time;
|
||||
time.setTime_t(timestamp);
|
||||
return tr("A previous operation created a backup of your mod list on %1.<br>"
|
||||
"This backup contains both the info which mods are enabled and the ordering.<br>"
|
||||
"You can restore that backup here.").arg(time.toString());
|
||||
} break;
|
||||
case PROBLEM_PROFILETWEAKS: {
|
||||
QString fileContent = readFileText(m_MOInfo->profilePath() + "/profile_tweaks.ini");
|
||||
return tr("Settings provided in ini tweaks have been overwritten in-game or in an applications.<br>"
|
||||
@@ -517,51 +529,35 @@ QString DiagnoseBasic::fullDescription(unsigned int key) const
|
||||
|
||||
bool DiagnoseBasic::hasGuidedFix(unsigned int key) const
|
||||
{
|
||||
return /*(key == PROBLEM_ASSETORDER) || */ (key == PROBLEM_MODLISTBACKUP) || (key == PROBLEM_PROFILETWEAKS);
|
||||
return (key == PROBLEM_ASSETORDER) || (key == PROBLEM_PROFILETWEAKS);
|
||||
}
|
||||
|
||||
void DiagnoseBasic::startGuidedFix(unsigned int key) const
|
||||
{
|
||||
switch (key) {
|
||||
/* case PROBLEM_ASSETORDER: {
|
||||
* if (QMessageBox::warning(NULL, tr("Continue?"), tr("This <b>BETA</b> feature will rearrange your mods to eliminate all "
|
||||
* "possible ordering conflicts. A backup of your mod list will be created. Proceed?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
* shellCopy(QStringList(m_MOInfo->profilePath() + "/modlist.txt"),
|
||||
* QStringList(m_MOInfo->profilePath() + "/modlist.txt_backup_" + QString("%1").arg(QDateTime::currentDateTime().toTime_t())));
|
||||
* foreach (const Move &op, m_SuggestedMoves) {
|
||||
* int oldPriority = m_MOInfo->modList()->priority(op.item.modName);
|
||||
* int targetPriority = -1;
|
||||
* if (op.type == Move::BEFORE) {
|
||||
* targetPriority = m_MOInfo->modList()->priority(op.reference.modName);
|
||||
* } else {
|
||||
* targetPriority = m_MOInfo->modList()->priority(op.reference.modName) + 1;
|
||||
* }
|
||||
* if (oldPriority < targetPriority) {
|
||||
* --targetPriority;
|
||||
* }
|
||||
* m_MOInfo->modList()->setPriority(op.item.modName, targetPriority);
|
||||
* }
|
||||
* }
|
||||
* } break;*/
|
||||
case PROBLEM_MODLISTBACKUP: {
|
||||
QMessageBox question(QMessageBox::Question, tr("Restore backup?"),
|
||||
tr("Do you want to restore this backup or delete it?"),
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||
question.setButtonText(QMessageBox::Yes, tr("Restore"));
|
||||
question.setButtonText(QMessageBox::No, tr("Delete"));
|
||||
|
||||
question.exec();
|
||||
if (question.result() == QMessageBox::Yes) {
|
||||
shellMove(QStringList(m_MOInfo->profilePath() + "/" + m_NewestModlistBackup), QStringList(m_MOInfo->profilePath() + "/modlist.txt"));
|
||||
m_MOInfo->refreshModList(false);
|
||||
} else if (question.result() == QMessageBox::No) {
|
||||
shellDelete(QStringList(m_MOInfo->profilePath() + "/" + m_NewestModlistBackup));
|
||||
case PROBLEM_ASSETORDER: {
|
||||
if (QMessageBox::warning(NULL, tr("Continue?"), tr("This <b>BETA</b> feature will rearrange your mods to eliminate all "
|
||||
"possible ordering conflicts. Proceed?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
foreach (const Move &op, m_SuggestedMoves) {
|
||||
int oldPriority = m_MOInfo->modList()->priority(op.item.modName);
|
||||
int targetPriority = -1;
|
||||
if (op.type == Move::BEFORE) {
|
||||
targetPriority = m_MOInfo->modList()->priority(op.reference.modName);
|
||||
} else {
|
||||
targetPriority = m_MOInfo->modList()->priority(op.reference.modName) + 1;
|
||||
}
|
||||
if (oldPriority < targetPriority) {
|
||||
--targetPriority;
|
||||
}
|
||||
m_MOInfo->modList()->setPriority(op.item.modName, targetPriority);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case PROBLEM_PROFILETWEAKS: {
|
||||
shellDeleteQuiet(m_MOInfo->profilePath() + "/profile_tweaks.ini");
|
||||
} break;
|
||||
default: throw MyException(tr("invalid problem key %1").arg(key));
|
||||
default:
|
||||
throw MyException(tr("invalid problem key %1").arg(key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -74,7 +74,6 @@ private:
|
||||
static const unsigned int PROBLEM_INVALIDFONT = 3;
|
||||
static const unsigned int PROBLEM_NITPICKINSTALLED = 4;
|
||||
static const unsigned int PROBLEM_ASSETORDER = 5;
|
||||
static const unsigned int PROBLEM_MODLISTBACKUP = 6;
|
||||
static const unsigned int PROBLEM_PROFILETWEAKS = 7;
|
||||
|
||||
static const unsigned int NUM_CONTEXT_ROWS = 5;
|
||||
@@ -86,8 +85,8 @@ private:
|
||||
QString modName;
|
||||
int pluginPriority;
|
||||
int modPriority;
|
||||
int sortPriority;
|
||||
int sortGroup;
|
||||
bool avoidMove;
|
||||
QSet<QString> relevantScripts;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user