Re-implemented plugin loading.

Using a multi-threaded loader member function from the game class.

This currently produces invalid cyclic errors for me though, so I need
to fix that.
This commit is contained in:
WrinklyNinja
2014-07-13 22:17:19 +01:00
parent 155a87321a
commit 2b90fc6c8b
3 changed files with 70 additions and 134 deletions
+33 -7
View File
@@ -31,6 +31,7 @@
#include "streams.h"
#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>
using namespace std;
@@ -572,19 +573,44 @@ namespace loot {
}
void Game::LoadPlugins(bool headersOnly) {
//Add all plugins in data folder not already in the hashset to the hashset, and load them.
for (fs::directory_iterator it(DataPath()); it != fs::directory_iterator(); ++it) {
boost::thread_group group;
size_t meanFileSize = 0;
unordered_map<std::string, size_t> tempMap;
std::set<std::string> skipPlugins;
//First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation.
for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && IsPlugin(it->path().string())) {
const string filename = it->path().filename().string();
if (plugins.find(filename) == plugins.end())
plugins.insert(std::pair<string, Plugin>(filename, Plugin(filename)));
size_t fileSize = fs::file_size(it->path());
meanFileSize += fileSize;
tempMap.emplace(it->path().filename().string(), fileSize);
}
}
meanFileSize /= tempMap.size();
for (auto &pluginPair: plugins) {
pluginPair.second = Plugin(*this, pluginPair.second.Name(), headersOnly);
//Now load plugins.
for (const auto &pluginPair : tempMap) {
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << pluginPair.first;
auto plugin = plugins.emplace(pluginPair.first, Plugin(pluginPair.first));
if (pluginPair.second > meanFileSize) {
skipPlugins.insert(pluginPair.first);
group.create_thread([this, &plugin, headersOnly]() {
plugin.first->second = Plugin(*this, plugin.first->first, headersOnly);
});
}
}
group.create_thread([this, &skipPlugins, headersOnly]() {
for (auto &pluginPair : this->plugins) {
if (skipPlugins.find(pluginPair.first) == skipPlugins.end()) {
pluginPair.second = Plugin(*this, pluginPair.first, headersOnly);
}
}
});
group.join_all();
}
void Game::CreateLOOTGameFolder() {
+2 -54
View File
@@ -78,56 +78,6 @@ namespace loot {
std::string revision;
std::string date;
};
/*
class PluginCache {
bool IsActive(const std::string& plugin) const;
void GetLoadOrder(std::list<std::string>& loadOrder) const;
void SetLoadOrder(const std::list<Plugin>& loadOrder) const; //Modifies game load order, even though const.
void RefreshActivePluginsList();
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
void LoadPlugins(bool headersOnly); //Loads all installed plugins.
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
std::unordered_set<std::string> activePlugins; //Holds lowercased strings.
};
*/
// A couple of plugin loader classes for handling plugin loading in separate threads.
class PluginLoader {
public:
PluginLoader(Plugin& plugin, Game& game) : _plugin(plugin), _game(game) {
}
void operator () () {
_plugin = Plugin(_game, _plugin.Name(), false);
}
Plugin& _plugin;
Game& _game;
std::string _filename;
bool _b;
};
class PluginsLoader {
public:
PluginsLoader(std::list<Plugin>& plugins, Game& game) : _plugins(plugins), _game(game) {}
void operator () () {
for (auto &plugin : _plugins) {
if (skipPlugins.find(plugin.Name()) == skipPlugins.end()) {
plugin = Plugin(_game, plugin.Name(), false);
}
}
}
std::list<Plugin>& _plugins;
Game& _game;
std::set<std::string> skipPlugins;
};
class Game {
public:
@@ -159,7 +109,6 @@ namespace loot {
boost::filesystem::path UserlistPath() const;
boost::filesystem::path ReportDataPath() const;
//TO BE REMOVED
//Game plugin functions.
bool IsActive(const std::string& plugin) const;
@@ -173,12 +122,11 @@ namespace loot {
//Caches for condition results, active plugins and CRCs.
std::unordered_map<std::string, bool> conditionCache; //Holds lowercased strings.
std::unordered_map<std::string, uint32_t> crcCache; //Holds lowercased strings.
//END TO BE REMOVED
//Plugin data and metadata lists.
Masterlist masterlist;
MetadataList userlist;
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited. TO BE REMOVED
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
espm::Settings espm_settings;
@@ -200,7 +148,7 @@ namespace loot {
boost::filesystem::path gamePath; //Path to the game's folder.
std::unordered_set<std::string> activePlugins; //Holds lowercased strings. TO BE REMOVED
std::unordered_set<std::string> activePlugins; //Holds lowercased strings.
//Creates directory in LOOT folder for LOOT's game-specific files.
void CreateLOOTGameFolder();
+35 -73
View File
@@ -607,7 +607,6 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
BOOST_LOG_TRIVIAL(debug) << "Beginning sorting process.";
list<loot::Message> messages;
list<loot::Plugin> plugins;
boost::thread_group group;
unsigned int lang;
@@ -633,38 +632,9 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("Masterlist parsing failed. Details: %1%")) % e.what()).str()));
}
});
//First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation.
size_t meanFileSize = 0;
boost::unordered_map<std::string, size_t> tempMap;
for (fs::directory_iterator it(_game->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && IsPlugin(it->path().string())) {
size_t fileSize = fs::file_size(it->path());
meanFileSize += fileSize;
tempMap.emplace(it->path().filename().string(), fileSize);
}
}
meanFileSize /= tempMap.size();
//Now load plugins.
PluginsLoader pll(plugins, *_game);
for (const auto &pluginPair: tempMap) {
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << pluginPair.first;
plugins.push_back(loot::Plugin(pluginPair.first));
if (pluginPair.second > meanFileSize) {
pll.skipPlugins.insert(pluginPair.first);
PluginLoader pl(plugins.back(), *_game);
group.create_thread(pl);
}
progDia->Pulse();
}
group.create_thread(pll);
group.create_thread([this]() {
this->_game->LoadPlugins(false);
});
group.join_all();
//Now load userlist.
@@ -685,48 +655,28 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
// Merge & Check Metadata
///////////////////////////////////////////////////////
if (fs::exists(_game->MasterlistPath()) || fs::exists(_game->UserlistPath())) {
//Merge all global message lists.
BOOST_LOG_TRIVIAL(debug) << "Merging all global message lists.";
if (!_game->masterlist.messages.empty())
messages.insert(messages.end(), _game->masterlist.messages.begin(), _game->masterlist.messages.end());
if (!_game->userlist.messages.empty())
messages.insert(messages.end(), _game->userlist.messages.begin(), _game->userlist.messages.end());
//Merge all global message lists.
BOOST_LOG_TRIVIAL(debug) << "Merging all global message lists.";
if (!_game->masterlist.messages.empty())
messages.insert(messages.end(), _game->masterlist.messages.begin(), _game->masterlist.messages.end());
if (!_game->userlist.messages.empty())
messages.insert(messages.end(), _game->userlist.messages.begin(), _game->userlist.messages.end());
//Evaluate any conditions in the global messages.
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
try {
list<loot::Message>::iterator it=messages.begin();
while (it != messages.end()) {
if (!it->EvalCondition(*_game, lang))
it = messages.erase(it);
else
++it;
}
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
}
//Merge plugin list and masterlist.
BOOST_LOG_TRIVIAL(debug) << "Merging plugin list and masterlist data.";
for (auto &plugin : plugins) {
BOOST_LOG_TRIVIAL(trace) << "Merging for plugin \"" << plugin.Name() << "\"";
//Check if there is a plugin entry in the masterlist. This will also find matching regex entries.
list<loot::Plugin>::iterator pos = std::find(_game->masterlist.plugins.begin(), _game->masterlist.plugins.end(), plugin);
if (pos != _game->masterlist.plugins.end()) {
BOOST_LOG_TRIVIAL(trace) << "Merging masterlist data down to plugin list data.";
plugin.MergeMetadata(*pos);
}
progDia->Pulse();
//Evaluate any conditions in the global messages.
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
try {
list<loot::Message>::iterator it=messages.begin();
while (it != messages.end()) {
if (!it->EvalCondition(*_game, lang))
it = messages.erase(it);
else
++it;
}
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
}
progDia->Update(800, translate("Building plugin graph..."));
@@ -745,6 +695,10 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
*/
//Check for back-edges, then perform a topological sort.
list<loot::Plugin> plugins;
for (auto &plugin : _game->plugins) {
plugins.push_back(plugin.second);
}
try {
bool applyLoadOrder = false;
@@ -760,8 +714,16 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
for (boost::tie(vit, vitend) = boost::vertices(graph); vit != vitend; ++vit) {
BOOST_LOG_TRIVIAL(trace) << "Merging for plugin \"" << graph[*vit].Name() << "\"";
//Check if there is a plugin entry in the masterlist. This will also find matching regex entries.
list<loot::Plugin>::iterator pos = std::find(_game->masterlist.plugins.begin(), _game->masterlist.plugins.end(), graph[*vit]);
if (pos != _game->masterlist.plugins.end()) {
BOOST_LOG_TRIVIAL(trace) << "Merging masterlist data down to plugin list data.";
graph[*vit].MergeMetadata(*pos);
}
//Check if there is a plugin entry in the userlist. This will also find matching regex entries.
list<loot::Plugin>::iterator pos = std::find(_game->userlist.plugins.begin(), _game->userlist.plugins.end(), graph[*vit]);
pos = std::find(_game->userlist.plugins.begin(), _game->userlist.plugins.end(), graph[*vit]);
if (pos != _game->userlist.plugins.end() && pos->Enabled()) {
BOOST_LOG_TRIVIAL(trace) << "Merging userlist data down to plugin list data.";