1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
#include "ConditionTester.h"
#include "ui/FomodViewModel.h"
#include <iplugingame.h>
#include <ipluginlist.h>
std::string setToString(const std::set<int> &set)
{
std::string str;
for (const auto& i : set) {
str += std::to_string(i) + ", ";
}
return str;
}
bool ConditionTester::isStepVisible(const std::shared_ptr<FlagMap>& flags,
const CompositeDependency& compositeDependency,
const int stepIndex,
const std::vector<std::shared_ptr<StepViewModel>>& steps) const
{
// first things first: is it visible?
if (!testCompositeDependency(flags, compositeDependency)) {
return false;
}
const auto flagDependencies = compositeDependency.flagDependencies;
if (flagDependencies.empty()) {
return true;
}
std::set<int> stepsThatSetThisFlag;
for (const auto& flagDependency : flagDependencies) {
// for this flag, find the plugins that set it
for (int i = stepIndex - 1; i >= 0; --i) {
for (const auto& group : steps[i]->getGroups()) {
for (const auto& plugin : group->getPlugins()) {
if (std::ranges::any_of(plugin->getPlugin()->conditionFlags.flags,
[&flagDependency](const ConditionFlag& flag) {
return flag.name == flagDependency.flag && flag.value == flagDependency.value;
})) {
stepsThatSetThisFlag.insert(i);
}
}
}
}
}
const auto anyVisible = std::ranges::any_of(stepsThatSetThisFlag, [this, &steps, &flags](const int index) {
return isStepVisible(flags, steps[index]->getVisibilityConditions(), index, steps);
});
if (!anyVisible) {
log.logMessage(DEBUG, "Step " + steps[stepIndex]->getName() + " has no dependent steps that are visible.");
log.logMessage(DEBUG, "Steps that set this flag: " + setToString(stepsThatSetThisFlag));
}
return anyVisible;
}
bool ConditionTester::testCompositeDependency(const std::shared_ptr<FlagMap>& flags,
const CompositeDependency& compositeDependency) const
{
const auto fileDependencies = compositeDependency.fileDependencies;
const auto flagDependencies = compositeDependency.flagDependencies;
const auto gameDependencies = compositeDependency.gameDependencies;
const auto nestedDependencies = compositeDependency.nestedDependencies;
const auto globalOperatorType = compositeDependency.operatorType;
// For the globalOperatorType
// Evaluate all conditions and store the results in a vector<bool>, then return based on operator.
// These aren't expensive to calculate so rather than do some fancy logic to short-circuit, just calculate all of 'em.
std::vector<bool> results;
for (const auto& fileDependency : fileDependencies) {
results.emplace_back(testFileDependency(fileDependency));
}
for (const auto& flagDependency : flagDependencies) {
results.emplace_back(testFlagDependency(flags, flagDependency));
}
for (const auto& gameDependency : gameDependencies) {
results.emplace_back(testGameDependency(gameDependency));
}
for (const auto& nestedDependency : nestedDependencies) {
results.emplace_back(testCompositeDependency(flags, nestedDependency));
}
if (globalOperatorType == OperatorTypeEnum::AND) {
return std::ranges::all_of(results, [](const bool result) { return result; });
}
return std::ranges::any_of(results, [](const bool result) { return result; });
}
bool ConditionTester::testFlagDependency(const std::shared_ptr<FlagMap>& flags, const FlagDependency& flagDependency)
{
// Every instance of this flag being set in the map.
const auto flagList = flags->getFlagsByKey(flagDependency.flag);
// Find the first instance of this flag being set (in the order specified by getFlagsByKey)
if (flagList.empty()) {
// If the dependency value is an empty string, it means this flag should be unset.
// So if we don't have any value for this flag, the result is true.
return flagDependency.value.empty();
}
return flagList.front().second == flagDependency.value;
}
bool ConditionTester::testFileDependency(const FileDependency& fileDependency) const
{
const std::string& pluginName = fileDependency.file;
const auto pluginState = getFileDependencyStateForPlugin(pluginName);
return pluginState == fileDependency.state;
}
bool ConditionTester::testGameDependency(const GameDependency& gameDependency) const
{
const auto gameVersion = mOrganizer->managedGame()->gameVersion().toStdString();
log.logMessage(DEBUG, "Comparing condition version " + gameDependency.version + " against " + gameVersion);
if ( gameDependency.version <= gameVersion) {
log.logMessage(DEBUG, "Version matches!");
}
return gameDependency.version <= gameVersion;
}
FileDependencyTypeEnum ConditionTester::getFileDependencyStateForPlugin(const std::string& pluginName) const
{
if (const auto it = pluginStateCache.find(pluginName); it != pluginStateCache.end()) {
return it->second;
}
const QFlags<MOBase::IPluginList::PluginState> pluginState = mOrganizer->pluginList()->state(
QString::fromStdString(pluginName));
FileDependencyTypeEnum state;
if (pluginState == MOBase::IPluginList::STATE_MISSING) {
state = FileDependencyTypeEnum::Missing;
} else if (pluginState == MOBase::IPluginList::STATE_INACTIVE) {
state = FileDependencyTypeEnum::Inactive;
} else if (pluginState == MOBase::IPluginList::STATE_ACTIVE) {
state = FileDependencyTypeEnum::Active;
} else {
state = FileDependencyTypeEnum::UNKNOWN_STATE;
}
pluginStateCache[pluginName] = state;
return state;
}
PluginTypeEnum ConditionTester::getPluginTypeDescriptorState(const std::shared_ptr<Plugin>& plugin,
const std::shared_ptr<FlagMap>& flags) const
{
// NOTE: A plugin's ConditionFlags aren't the same thing as a step visibility one.
// A plugin's ConditionFlags are toggled based on the selection state of the plugin
// We only evaluate the typeDescriptor here.
// We will return the 'winning' type or the default. If multiple conditions are met,
// ...well, I'm not sure.
// ReSharper disable once CppTooWideScopeInitStatement
const auto& dependencyType = plugin->typeDescriptor.dependencyType;
for (const auto& pattern : dependencyType.patterns.patterns) {
if (testCompositeDependency(flags, pattern.dependencies)) {
return pattern.type;
}
}
// Sometimes authors do this.
if (plugin->typeDescriptor.type != PluginTypeEnum::Optional) {
return plugin->typeDescriptor.type;
}
if (plugin->typeDescriptor.dependencyType.defaultType.has_value()) {
return plugin->typeDescriptor.dependencyType.defaultType.value();
}
return PluginTypeEnum::Optional;
}
|