Use existing load order when sorting.

To decide the relative positions of otherwise non-interacting plugins,
or conflicting plugins that have equal numbers of override records. For
This commit is contained in:
Oliver Hamlet
2015-04-30 12:51:39 +01:00
parent 8d69b942df
commit 509e341c82
4 changed files with 68 additions and 22 deletions
+8 -1
View File
@@ -817,8 +817,15 @@ namespace loot {
graph[v].CheckInstallValidity(*this);
}
// Get the existing load order.
list<string> loadorder;
GetLoadOrder(loadorder);
BOOST_LOG_TRIVIAL(info) << "Fetched existing load order: ";
for (const auto &plugin : loadorder)
BOOST_LOG_TRIVIAL(info) << plugin;
// Now add edges and sort.
progressCallback(lc::translate("Adding edges to plugin graph and performing topological sort..."));
return loot::Sort(graph);
return loot::Sort(graph, loadorder);
}
}
+50 -7
View File
@@ -290,13 +290,10 @@ namespace loot {
parentVertex = *vit2;
vertex = *vit;
}
else if (graph[*vit].Name() < graph[*vit2].Name()) { //There needs to be an edge between the two, but direction cannot be decided using overlap size. Just use names.
parentVertex = *vit;
vertex = *vit2;
}
else {
parentVertex = *vit2;
vertex = *vit;
// There's no way to determine the order between the two, so just leave them to be treated like
// any two unlinked, non-conflicting plugins in the next pass.
continue;
}
//BOOST_LOG_TRIVIAL(trace) << "Checking edge validity between \"" << graph[*vit].Name() << "\" and \"" << graph[*vit2].Name() << "\".";
@@ -310,7 +307,50 @@ namespace loot {
}
}
std::list<Plugin> Sort(PluginGraph& graph) {
size_t LoadOrderPos(const list<string>& loadorder, const std::string& plugin) {
auto it = find(loadorder.begin(), loadorder.end(), plugin);
if (it != loadorder.end())
return distance(loadorder.begin(), it);
else
throw error(error::sorting_error, "No existing load order position for " + plugin);
}
void AddTieBreakEdges(PluginGraph& graph, const list<string>& loadorder, const vertex_map_t& v_index_map) {
// In order for the sort to be performed stably, there must be only one possible result.
// This can be enforced by adding edges between all vertices that aren't already linked.
// Use existing load order to decide the direction of these edges.
loot::vertex_it vit, vitend;
for (boost::tie(vit, vitend) = boost::vertices(graph); vit != vitend; ++vit) {
BOOST_LOG_TRIVIAL(trace) << "Adding tie-break edges to vertex for \"" << graph[*vit].Name() << "\".";
loot::vertex_it vit2, vitend2;
for (boost::tie(vit2, vitend2) = boost::vertices(graph); vit2 != vitend2; ++vit2) {
if (vit == vit2 || boost::edge(*vit, *vit2, graph).second || boost::edge(*vit2, *vit, graph).second)
//Vertices are the same or are already linked.
continue;
vertex_t vertex, parentVertex;
if (LoadOrderPos(loadorder, graph[*vit].Name()) < LoadOrderPos(loadorder, graph[*vit2].Name())) {
parentVertex = *vit;
vertex = *vit2;
}
else {
parentVertex = *vit2;
vertex = *vit;
}
//BOOST_LOG_TRIVIAL(trace) << "Checking edge validity between \"" << graph[*vit].Name() << "\" and \"" << graph[*vit2].Name() << "\".";
if (!EdgeCreatesCycle(parentVertex, vertex, graph, v_index_map)) { //No edge going the other way, OK to add this edge.
BOOST_LOG_TRIVIAL(trace) << "Adding edge from \"" << graph[parentVertex].Name() << "\" to \"" << graph[vertex].Name() << "\".";
boost::add_edge(parentVertex, vertex, graph);
}
}
}
}
std::list<Plugin> Sort(PluginGraph& graph, const list<string>& loadorder) {
// Prebuild an index map, which std::list-based VertexList graphs don't have.
map<vertex_t, size_t> index_map;
vertex_map_t v_index_map(index_map);
@@ -329,6 +369,9 @@ namespace loot {
BOOST_LOG_TRIVIAL(debug) << "Adding overlap edges.";
AddOverlapEdges(graph, v_index_map);
BOOST_LOG_TRIVIAL(debug) << "Adding tie-break edges.";
AddTieBreakEdges(graph, loadorder, v_index_map);
BOOST_LOG_TRIVIAL(info) << "Checking to see if the graph is cyclic.";
CheckForCycles(graph, v_index_map);
+2 -3
View File
@@ -20,7 +20,7 @@
You should have received a copy of the GNU General Public License
along with LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
*/
#ifndef __LOOT_GRAPH__
#define __LOOT_GRAPH__
@@ -33,11 +33,10 @@
#include <boost/graph/graphviz.hpp>
namespace loot {
typedef boost::adjacency_list<boost::listS, boost::listS, boost::directedS, loot::Plugin> PluginGraph;
typedef boost::graph_traits<PluginGraph>::vertex_descriptor vertex_t;
std::list<Plugin> Sort(PluginGraph& graph);
std::list<Plugin> Sort(PluginGraph& graph, const std::list<std::string>& loadorder);
}
#endif
+8 -11
View File
@@ -360,17 +360,17 @@ TEST_F(OblivionAPIOperationsTest, SortPlugins) {
// Expected order was obtained from running the API function once.
std::list<std::string> expectedOrder = {
"Oblivion.esm",
"Blank.esm",
"Blank - Different.esm",
"Blank - Different Master Dependent.esm",
"Blank - Master Dependent.esm",
"Oblivion.esm",
"Blank - Different Master Dependent.esm",
"Blank.esp",
"Blank - Different.esp",
"Blank - Different Master Dependent.esp",
"Blank - Different Plugin Dependent.esp",
"Blank - Master Dependent.esp",
"Blank - Different Master Dependent.esp",
"Blank - Plugin Dependent.esp",
"Blank - Different Plugin Dependent.esp",
};
std::list<std::string> actualOrder;
for (size_t i = 0; i < numPlugins; ++i) {
@@ -391,20 +391,17 @@ TEST_F(SkyrimAPIOperationsTest, SortPlugins) {
// Expected order was obtained from running the API function once.
std::list<std::string> expectedOrder = {
"Skyrim.esm",
"Blank.esm",
"Blank - Different.esm",
"Blank - Different Master Dependent.esm",
"Blank - Master Dependent.esm",
"Skyrim.esm",
"Blank - Different Master Dependent.esm",
"Blank.esp",
"Blank - Different.esp",
"Blank - Different Master Dependent.esp",
"Blank - Different Plugin Dependent.esp",
"Blank - Master Dependent.esp",
"Blank - Plugin Dependent.esp",
"Blank - Different Master Dependent.esp",
"Blank - Different.esp",
"Blank - Different Plugin Dependent.esp"
"Blank - Plugin Dependent.esp",
"Blank - Different Plugin Dependent.esp",
};
std::list<std::string> actualOrder;
for (size_t i = 0; i < numPlugins; ++i) {