mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Minor optimisations to adding hardcoded edges
It only speeds up sorting by about 3% with my 1619 plugin load order, but I think the logic is simpler despite all the iterators involved.
This commit is contained in:
@@ -76,6 +76,13 @@ For each plugin:
|
||||
4. Add edges coming from all the plugin's load after files that are installed
|
||||
plugins.
|
||||
|
||||
Hardcoded edges
|
||||
---------------
|
||||
|
||||
Some games hardcode certain plugins to load in certain positions, and this
|
||||
section adds edges in the correct order between those plugins, and between those
|
||||
plugins and the rest of the plugins in the graph.
|
||||
|
||||
Group edges
|
||||
-----------
|
||||
|
||||
|
||||
@@ -636,9 +636,8 @@ void PluginGraph::AddSpecificEdges() {
|
||||
}
|
||||
}
|
||||
|
||||
void PluginGraph::AddHardcodedPluginEdges(const Game& game) {
|
||||
using std::filesystem::u8path;
|
||||
|
||||
void PluginGraph::AddHardcodedPluginEdges(
|
||||
const std::vector<std::string>& hardcodedPlugins) {
|
||||
const auto logger = getLogger();
|
||||
if (logger) {
|
||||
logger->trace(
|
||||
@@ -646,45 +645,65 @@ void PluginGraph::AddHardcodedPluginEdges(const Game& game) {
|
||||
"positions...");
|
||||
}
|
||||
|
||||
const auto implicitlyActivePlugins =
|
||||
game.GetLoadOrderHandler().GetImplicitlyActivePlugins();
|
||||
if (hardcodedPlugins.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::set<std::string> processedPluginPaths;
|
||||
for (const auto& plugin : implicitlyActivePlugins) {
|
||||
processedPluginPaths.insert(NormalizeFilename(plugin));
|
||||
std::map<std::vector<std::string>::const_iterator, vertex_t>
|
||||
implicitlyActivePluginVertices;
|
||||
std::vector<vertex_t> otherPluginVertices;
|
||||
|
||||
if (game.Type() == GameType::tes5 &&
|
||||
loot::equivalent(game.DataPath() / u8path(plugin),
|
||||
game.DataPath() / "update.esm")) {
|
||||
if (logger) {
|
||||
logger->debug(
|
||||
"Skipping adding hardcoded plugin edges for Update.esm as it does "
|
||||
"not have a hardcoded position for Skyrim.");
|
||||
continue;
|
||||
// Build the vertex map for implicitly active plugins and record the vertices
|
||||
// for other plugins.
|
||||
for (const auto& vertex : boost::make_iterator_range(GetVertices())) {
|
||||
const auto pluginName = GetPlugin(vertex).GetName();
|
||||
const auto it =
|
||||
std::find_if(hardcodedPlugins.begin(),
|
||||
hardcodedPlugins.end(),
|
||||
[&](const std::string& name) {
|
||||
return CompareFilenames(name, pluginName) == 0;
|
||||
});
|
||||
|
||||
if (it != hardcodedPlugins.end()) {
|
||||
implicitlyActivePluginVertices.emplace(it, vertex);
|
||||
} else {
|
||||
otherPluginVertices.push_back(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
if (implicitlyActivePluginVertices.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now add edges between consecutive implicitly active plugins.
|
||||
auto lastImplicitlyActiveVertexIt = implicitlyActivePluginVertices.end();
|
||||
for (auto it = hardcodedPlugins.begin(); it != hardcodedPlugins.end();) {
|
||||
const auto fromVertexIt = implicitlyActivePluginVertices.find(it);
|
||||
|
||||
// Find the next valid implicitly active plugin and its vertex.
|
||||
auto toVertexIt = implicitlyActivePluginVertices.end();
|
||||
for (it = std::next(it); it != hardcodedPlugins.end(); ++it) {
|
||||
toVertexIt = implicitlyActivePluginVertices.find(it);
|
||||
if (toVertexIt != implicitlyActivePluginVertices.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto pluginVertex = GetVertexByName(plugin);
|
||||
if (fromVertexIt != implicitlyActivePluginVertices.end()) {
|
||||
lastImplicitlyActiveVertexIt = fromVertexIt;
|
||||
|
||||
if (!pluginVertex.has_value()) {
|
||||
if (logger) {
|
||||
logger->debug(
|
||||
"Skipping adding hardcoded plugin edges for \"{}\" as it has not "
|
||||
"been loaded.",
|
||||
plugin);
|
||||
if (toVertexIt != implicitlyActivePluginVertices.end()) {
|
||||
AddEdge(fromVertexIt->second, toVertexIt->second, EdgeType::hardcoded);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& vertex : boost::make_iterator_range(GetVertices())) {
|
||||
if (vertex == pluginVertex.value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processedPluginPaths.count(
|
||||
NormalizeFilename(GetPlugin(vertex).GetName())) == 0) {
|
||||
AddEdge(pluginVertex.value(), vertex, EdgeType::hardcoded);
|
||||
}
|
||||
// Finally, add edges from the last implicitly active plugin to the other
|
||||
// plugins.
|
||||
if (lastImplicitlyActiveVertexIt != implicitlyActivePluginVertices.end()) {
|
||||
for (const auto& vertex : otherPluginVertices) {
|
||||
AddEdge(
|
||||
lastImplicitlyActiveVertexIt->second, vertex, EdgeType::hardcoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ public:
|
||||
void AddVertex(const PluginSortingData& plugin);
|
||||
|
||||
void AddSpecificEdges();
|
||||
void AddHardcodedPluginEdges(const Game& game);
|
||||
void AddHardcodedPluginEdges(
|
||||
const std::vector<std::string>& hardcodedPlugins);
|
||||
void AddGroupEdges(const std::unordered_map<std::string, Group>& groups);
|
||||
void AddOverlapEdges();
|
||||
void AddTieBreakEdges();
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "plugin_sort.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "api/helpers/logging.h"
|
||||
#include "api/sorting/group_sort.h"
|
||||
#include "api/sorting/plugin_graph.h"
|
||||
@@ -192,16 +194,30 @@ std::vector<PluginSortingData> GetPluginsSortingData(
|
||||
return pluginsSortingData;
|
||||
}
|
||||
|
||||
std::vector<std::string> SortPluginGraph(PluginGraph& graph, const Game& game) {
|
||||
std::vector<std::string> GetPluginsWithHardcodedPositions(const Game& game) {
|
||||
auto plugins = game.GetLoadOrderHandler().GetImplicitlyActivePlugins();
|
||||
|
||||
if (game.Type() == GameType::tes5) {
|
||||
auto newEndIt = std::remove_if(
|
||||
plugins.begin(), plugins.end(), [](const std::string& plugin) {
|
||||
return boost::iequals(plugin, "update.esm");
|
||||
});
|
||||
|
||||
plugins.erase(newEndIt, plugins.end());
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
std::vector<std::string> SortPluginGraph(
|
||||
PluginGraph& graph,
|
||||
const std::vector<std::string>& hardcodedPlugins,
|
||||
const std::unordered_map<std::string, Group>& groupsMap) {
|
||||
// Now add the interactions between plugins to the graph as edges.
|
||||
graph.AddSpecificEdges();
|
||||
graph.AddHardcodedPluginEdges(game);
|
||||
graph.AddHardcodedPluginEdges(hardcodedPlugins);
|
||||
|
||||
std::unordered_map<std::string, Group> groups;
|
||||
for (const auto& group : game.GetDatabase().GetGroups()) {
|
||||
groups.emplace(group.GetName(), group);
|
||||
}
|
||||
graph.AddGroupEdges(groups);
|
||||
graph.AddGroupEdges(groupsMap);
|
||||
|
||||
// Check for cycles now because from this point on edges are only added if
|
||||
// they don't cause cycles, and adding tie-break edges is by far the slowest
|
||||
@@ -275,8 +291,17 @@ std::vector<std::string> SortPlugins(
|
||||
nonMastersGraph.AddVertex(*it);
|
||||
}
|
||||
|
||||
auto newLoadOrder = SortPluginGraph(mastersGraph, game);
|
||||
const auto newNonMastersLoadOrder = SortPluginGraph(nonMastersGraph, game);
|
||||
const auto hardcodedPlugins = GetPluginsWithHardcodedPositions(game);
|
||||
|
||||
std::unordered_map<std::string, Group> groupsMap;
|
||||
for (const auto& group : game.GetDatabase().GetGroups()) {
|
||||
groupsMap.emplace(group.GetName(), group);
|
||||
}
|
||||
|
||||
auto newLoadOrder =
|
||||
SortPluginGraph(mastersGraph, hardcodedPlugins, groupsMap);
|
||||
const auto newNonMastersLoadOrder =
|
||||
SortPluginGraph(nonMastersGraph, hardcodedPlugins, groupsMap);
|
||||
|
||||
newLoadOrder.insert(newLoadOrder.end(),
|
||||
newNonMastersLoadOrder.begin(),
|
||||
|
||||
Reference in New Issue
Block a user