Skip to content
This repository was archived by the owner on May 29, 2025. It is now read-only.

Commit 21a6cb2

Browse files
authored
Merge pull request #15 from ModOrganizer2/plugin_updates
Add diagnoses to Starfield related to plugins
2 parents 0d5a40b + a791abe commit 21a6cb2

File tree

2 files changed

+246
-3
lines changed

2 files changed

+246
-3
lines changed

src/gamestarfield.cpp

Lines changed: 218 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "gamestarfield.h"
22

3+
#include "ipluginlist.h"
4+
35
#include "starfieldbsainvalidation.h"
46
#include "starfielddataarchives.h"
57
#include "starfieldgameplugins.h"
@@ -16,6 +18,7 @@
1618
#include <pluginsetting.h>
1719

1820
#include <QCoreApplication>
21+
#include <QDesktopServices>
1922
#include <QDir>
2023
#include <QFileInfo>
2124
#include <QList>
@@ -26,6 +29,7 @@
2629
#include <memory>
2730

2831
#include "scopeguard.h"
32+
#include "utility.h"
2933

3034
using namespace MOBase;
3135

@@ -131,7 +135,19 @@ QList<PluginSetting> GameStarfield::settings() const
131135
tr("Turn on plugin management. As of Starfield 1.7.33 this "
132136
"REQUIRES fixing 'plugins.txt' with a SFSE plugin. This "
133137
"will do nothing otherwise."),
134-
false);
138+
false)
139+
<< PluginSetting(
140+
"enable_esp_warning",
141+
tr("Show a warning when ESP plugins are enabled in the load order."),
142+
true)
143+
<< PluginSetting(
144+
"enable_esl_warning",
145+
tr("Show a warning when light plugins are enabled in the load order."),
146+
true)
147+
<< PluginSetting("enable_overlay_warning",
148+
tr("Show a warning when overlay-flagged plugins ar enabled "
149+
"in the load order."),
150+
true);
135151
}
136152

137153
MappingType GameStarfield::mappings() const
@@ -307,3 +323,204 @@ int GameStarfield::nexusGameID() const
307323
{
308324
return 4187;
309325
}
326+
327+
// Start Diagnose
328+
std::vector<unsigned int> GameStarfield::activeProblems() const
329+
{
330+
std::vector<unsigned int> result;
331+
if (m_Organizer->managedGame() == this) {
332+
if (m_Organizer->pluginSetting(name(), "enable_esp_warning").toBool() &&
333+
activeESP())
334+
result.push_back(PROBLEM_ESP);
335+
if (m_Organizer->pluginSetting(name(), "enable_esl_warning").toBool() &&
336+
activeESL())
337+
result.push_back(PROBLEM_ESL);
338+
if (m_Organizer->pluginSetting(name(), "enable_overlay_warning").toBool() &&
339+
activeOverlay())
340+
result.push_back(PROBLEM_OVERLAY);
341+
if (testFilePresent())
342+
result.push_back(PROBLEM_TEST_FILE);
343+
else if (pluginsTxtEnabler())
344+
result.push_back(PROBLEM_PLUGINS_TXT);
345+
}
346+
return result;
347+
}
348+
349+
bool GameStarfield::activeESP() const
350+
{
351+
m_Active_ESPs.clear();
352+
std::set<QString> enabledPlugins;
353+
354+
QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool {
355+
return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity);
356+
});
357+
358+
for (const QString& esp : esps) {
359+
QString baseName = QFileInfo(esp).fileName();
360+
if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE) {
361+
m_Active_ESPs.insert(baseName);
362+
}
363+
}
364+
365+
if (!m_Active_ESPs.empty())
366+
return true;
367+
return false;
368+
}
369+
370+
bool GameStarfield::activeESL() const
371+
{
372+
m_Active_ESLs.clear();
373+
std::set<QString> enabledPlugins;
374+
375+
QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool {
376+
return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity) ||
377+
fileName.endsWith(".esm", FileNameComparator::CaseSensitivity) ||
378+
fileName.endsWith(".esl", FileNameComparator::CaseSensitivity);
379+
});
380+
381+
for (const QString& esp : esps) {
382+
QString baseName = QFileInfo(esp).fileName();
383+
if (primaryPlugins().contains(baseName, Qt::CaseInsensitive))
384+
continue;
385+
if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE &&
386+
!m_Organizer->pluginList()->hasNoRecords(baseName))
387+
if (m_Organizer->pluginList()->hasLightExtension(baseName) ||
388+
m_Organizer->pluginList()->isLightFlagged(baseName))
389+
m_Active_ESLs.insert(baseName);
390+
}
391+
392+
if (!m_Active_ESLs.empty())
393+
return true;
394+
return false;
395+
}
396+
397+
bool GameStarfield::activeOverlay() const
398+
{
399+
m_Active_Overlays.clear();
400+
std::set<QString> enabledPlugins;
401+
402+
QStringList esps = m_Organizer->findFiles("", [](const QString& fileName) -> bool {
403+
return fileName.endsWith(".esp", FileNameComparator::CaseSensitivity) ||
404+
fileName.endsWith(".esm", FileNameComparator::CaseSensitivity) ||
405+
fileName.endsWith(".esl", FileNameComparator::CaseSensitivity);
406+
});
407+
408+
for (const QString& esp : esps) {
409+
QString baseName = QFileInfo(esp).fileName();
410+
if (primaryPlugins().contains(baseName, Qt::CaseInsensitive))
411+
continue;
412+
if (m_Organizer->pluginList()->state(baseName) == IPluginList::STATE_ACTIVE) {
413+
if (m_Organizer->pluginList()->isOverlayFlagged(baseName))
414+
m_Active_Overlays.insert(baseName);
415+
}
416+
}
417+
418+
if (!m_Active_Overlays.empty())
419+
return true;
420+
return false;
421+
}
422+
423+
bool GameStarfield::testFilePresent() const
424+
{
425+
if (m_Organizer->pluginSetting(name(), "enable_plugin_management").toBool() &&
426+
!testFilePlugins().isEmpty())
427+
return true;
428+
return false;
429+
}
430+
431+
bool GameStarfield::pluginsTxtEnabler() const
432+
{
433+
if (sortMechanism() != SortMechanism::NONE) {
434+
auto files = m_Organizer->findFiles("sfse\\plugins", {"sfpluginstxtenabler.dll"});
435+
if (files.isEmpty())
436+
return true;
437+
}
438+
return false;
439+
}
440+
441+
QString GameStarfield::shortDescription(unsigned int key) const
442+
{
443+
switch (key) {
444+
case PROBLEM_ESP:
445+
return tr("You have active ESP plugins in Starfield");
446+
case PROBLEM_ESL:
447+
return tr("You have active ESL plugins in Starfield");
448+
case PROBLEM_OVERLAY:
449+
return tr("You have active overlay plugins");
450+
case PROBLEM_TEST_FILE:
451+
return tr("sTestFile entries are present");
452+
case PROBLEM_PLUGINS_TXT:
453+
return tr("Plugins.txt Enabler missing");
454+
}
455+
}
456+
457+
QString GameStarfield::fullDescription(unsigned int key) const
458+
{
459+
switch (key) {
460+
case PROBLEM_ESP: {
461+
QString espInfo = SetJoin(m_Active_ESPs, ", ");
462+
return tr("<p>ESP plugins are not ideal for Starfield. In addition to being unable "
463+
"to sort them alongside ESM or master-flagged plugins, certain record "
464+
"references are always kept loaded by the game. This consumes "
465+
"unnecessary resources and limits the game's ability to load what it "
466+
"needs.</p>"
467+
"<p>Ideally, plugins should be saved as ESM files upon release. It can "
468+
"also be released as an ESL plugin, however there are additional "
469+
"concerns with the way light plugins are currently handled and should "
470+
"only be used when absolutely certain about what you're doing.</p>"
471+
"<p>Notably, xEdit does not currently support saving ESP files.</p>"
472+
"<h4>Current ESPs:</h4><p>%1</p>")
473+
.arg(espInfo);
474+
}
475+
case PROBLEM_ESL: {
476+
QString eslInfo = SetJoin(m_Active_ESLs, ", ");
477+
return tr("<p>Light plugins work differently in Starfield. They use a different "
478+
"base form ID compared with standard plugin files.</p>"
479+
"<p>What this means is that you can't just change a standard plugin to a "
480+
"light plugin at will, it can and will break any dependent plugin. If "
481+
"you do so, be absolutely certain no other plugins use that plugin as a "
482+
"master.</p>"
483+
"<p>Notably, xEdit does not currently support saving or loading ESL "
484+
"files under these conditions.<p>"
485+
"<h4>Current ESLs:</h4><p>%1</p>")
486+
.arg(eslInfo);
487+
}
488+
case PROBLEM_OVERLAY: {
489+
QString overlayInfo = SetJoin(m_Active_Overlays, ", ");
490+
return tr("<p>Overlay-flagged plugins are not currently recommended. In theory, "
491+
"they should allow you to update existing records without utilizing "
492+
"additional load order slots. Unfortunately, it appears that the game "
493+
"still allocates the slots as if these were standard plugins. Therefore, "
494+
"at the moment there is no real use for this plugin flag.</p>"
495+
"<p>Notably, xEdit does not currently support saving or loading "
496+
"overlay-flagged files under these conditions.</p>"
497+
"<h4>Current Overlays:</h4><p>%1</p>")
498+
.arg(overlayInfo);
499+
}
500+
case PROBLEM_TEST_FILE: {
501+
return tr("<p>You have plugin managment enabled but you still have sTestFile "
502+
"settings in your StarfieldCustom.ini. These must be removed or the game "
503+
"will not read the plugins.txt file. Management is still disabled.</p>");
504+
}
505+
case PROBLEM_PLUGINS_TXT: {
506+
return tr("<p>You have plugin management turned on but do not have the Plugins.txt "
507+
"Enabler SFSE plugin installed. Plugin file management for Starfield "
508+
"will not work without this SFSE plugin.</p>");
509+
}
510+
}
511+
}
512+
513+
bool GameStarfield::hasGuidedFix(unsigned int key) const
514+
{
515+
if (key == PROBLEM_PLUGINS_TXT)
516+
return true;
517+
return false;
518+
}
519+
520+
void GameStarfield::startGuidedFix(unsigned int key) const
521+
{
522+
if (key == PROBLEM_PLUGINS_TXT) {
523+
QDesktopServices::openUrl(
524+
QUrl("https://www.nexusmods.com/starfield/mods/4157?tab=files"));
525+
}
526+
}

src/gamestarfield.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
#define GAMESTARFIELD_H
33

44
#include "gamegamebryo.h"
5+
#include "iplugindiagnose.h"
56

67
#include <QObject>
78
#include <QtGlobal>
89

9-
class GameStarfield : public GameGamebryo
10+
class GameStarfield : public GameGamebryo, public MOBase::IPluginDiagnose
1011
{
1112
Q_OBJECT
12-
13+
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame MOBase::IPluginDiagnose)
1314
Q_PLUGIN_METADATA(IID "org.modorganizer.GameStarfield" FILE "gamestarfield.json")
1415

1516
public:
@@ -45,6 +46,13 @@ class GameStarfield : public GameGamebryo
4546
virtual int nexusModOrganizerID() const override;
4647
virtual int nexusGameID() const override;
4748

49+
public: // IPluginDiagnose interface
50+
virtual std::vector<unsigned int> activeProblems() const override;
51+
virtual QString shortDescription(unsigned int key) const override;
52+
virtual QString fullDescription(unsigned int key) const override;
53+
virtual bool hasGuidedFix(unsigned int key) const override;
54+
virtual void startGuidedFix(unsigned int key) const override;
55+
4856
public: // IPlugin interface
4957
virtual QString name() const override;
5058
virtual QString localizedName() const override;
@@ -59,6 +67,24 @@ class GameStarfield : public GameGamebryo
5967
std::shared_ptr<const GamebryoSaveGame> makeSaveGame(QString filePath) const override;
6068
QString savegameExtension() const override;
6169
QString savegameSEExtension() const override;
70+
71+
private:
72+
bool activeESP() const;
73+
bool activeESL() const;
74+
bool activeOverlay() const;
75+
bool testFilePresent() const;
76+
bool pluginsTxtEnabler() const;
77+
78+
private:
79+
static const unsigned int PROBLEM_ESP = 1;
80+
static const unsigned int PROBLEM_ESL = 2;
81+
static const unsigned int PROBLEM_OVERLAY = 3;
82+
static const unsigned int PROBLEM_TEST_FILE = 4;
83+
static const unsigned int PROBLEM_PLUGINS_TXT = 5;
84+
85+
mutable std::set<QString> m_Active_ESPs;
86+
mutable std::set<QString> m_Active_ESLs;
87+
mutable std::set<QString> m_Active_Overlays;
6288
};
6389

6490
#endif // GAMEStarfield_H

0 commit comments

Comments
 (0)