Remove Plugin(string) constructor

Plugin objects are supposed to hold data for actual files, and having a
name but no loaded data is a state that they should never be in.
This commit is contained in:
Oliver Hamlet
2015-12-07 15:33:08 +00:00
parent 038587c3e2
commit 9d8d75615b
5 changed files with 90 additions and 110 deletions
+51 -25
View File
@@ -41,7 +41,7 @@ namespace lc = boost::locale;
namespace loot {
Game::Game() : _pluginsFullyLoaded(false) {}
Game::Game(const GameSettings& gameSettings) : GameSettings(gameSettings.Id(), gameSettings.FolderName()), _pluginsFullyLoaded(false) {
Game::Game(const GameSettings& gameSettings) : GameSettings(gameSettings), _pluginsFullyLoaded(false) {
this->SetName(gameSettings.Name())
.SetMaster(gameSettings.Master())
.SetRepoURL(gameSettings.RepoURL())
@@ -50,8 +50,31 @@ namespace loot {
.SetRegistryKey(gameSettings.RegistryKey());
}
Game::Game(const Game& game) :
GameSettings(game),
LoadOrderHandler(game),
GameCache(game),
masterlist(game.masterlist),
userlist(game.userlist),
plugins(game.plugins),
_pluginsFullyLoaded(game.ArePluginsFullyLoaded()) {}
Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder), _pluginsFullyLoaded(false) {}
Game& Game::operator= (const Game& game) {
if (&game != this) {
GameSettings::operator=(game);
LoadOrderHandler::operator=(game);
GameCache::operator=(game);
masterlist = game.masterlist;
userlist = game.userlist;
plugins = game.plugins;
_pluginsFullyLoaded = game.ArePluginsFullyLoaded();
}
return *this;
}
void Game::Init(bool createFolder, const boost::filesystem::path& gameLocalAppData) {
if (Id() != Game::tes4 && Id() != Game::tes5 && Id() != Game::fo3 && Id() != Game::fonv && Id() != Game::fo4) {
throw error(error::invalid_args, lc::translate("Invalid game ID supplied.").str());
@@ -118,29 +141,35 @@ namespace loot {
BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath();
for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && Plugin::IsValid(it->path().filename().string(), *this)) {
Plugin temp(it->path().filename().string());
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << temp.Name();
string name = it->path().filename().string();
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << name;
// Trim .ghost extension if present.
if (boost::iends_with(name, ".ghost"))
name = name.substr(0, name.length() - 6);
uintmax_t fileSize = fs::file_size(it->path());
meanFileSize += fileSize;
//Insert the lowercased name as a key for case-insensitive matching.
string name = boost::locale::to_lower(temp.Name());
plugins.insert(pair<string, Plugin>(name, temp));
sizeMap.insert(pair<uintmax_t, string>(fileSize, name));
sizeMap.emplace(fileSize, name);
}
}
meanFileSize /= sizeMap.size(); //Rounding error, but not important.
// Reserve space in the plugins unordered_map to speed up inserting
// later and more importantly avoid any inserts invalidating
// iterators.
plugins.reserve(sizeMap.size());
// Get the number of threads to use.
// hardware_concurrency() may be zero, if so then use only one thread.
size_t threadsToUse = std::min((size_t)thread::hardware_concurrency(), plugins.size());
size_t threadsToUse = std::min((size_t)thread::hardware_concurrency(), sizeMap.size());
threadsToUse = std::max(threadsToUse, (size_t)1);
// Divide the plugins up by thread.
unsigned int pluginsPerThread = ceil((double)plugins.size() / threadsToUse);
vector<vector<unordered_map<string, Plugin>::iterator>> pluginGroups(threadsToUse);
BOOST_LOG_TRIVIAL(info) << "Loading " << plugins.size() << " plugins using " << threadsToUse << " threads, with up to " << pluginsPerThread << " plugins per thread.";
unsigned int pluginsPerThread = ceil((double)sizeMap.size() / threadsToUse);
vector<vector<string>> pluginGroups(threadsToUse);
BOOST_LOG_TRIVIAL(info) << "Loading " << sizeMap.size() << " plugins using " << threadsToUse << " threads, with up to " << pluginsPerThread << " plugins per thread.";
// The plugins should be split between the threads so that the data
// load is as evenly spread as possible.
@@ -149,7 +178,7 @@ namespace loot {
if (currentGroup == threadsToUse)
currentGroup = 0;
BOOST_LOG_TRIVIAL(trace) << "Adding plugin " << plugin.second << " to loading group " << currentGroup;
pluginGroups[currentGroup].push_back(plugins.find(plugin.second));
pluginGroups[currentGroup].push_back(plugin.second);
++currentGroup;
}
@@ -157,19 +186,11 @@ namespace loot {
BOOST_LOG_TRIVIAL(trace) << "Starting plugin loading.";
vector<thread> threads;
while (threads.size() < threadsToUse) {
vector<unordered_map<string, Plugin>::iterator>& pluginGroup = pluginGroups[threads.size()];
threads.push_back(thread([this, &pluginGroup, headersOnly]() {
for (auto it : pluginGroup) {
BOOST_LOG_TRIVIAL(trace) << "Loading " << it->second.Name();
try {
it->second = Plugin(*this, it->second.Name(), headersOnly);
}
catch (exception &e) {
BOOST_LOG_TRIVIAL(error) << it->second.Name() << ": Exception occurred: " << e.what();
Plugin p(it->second.Name());
p.Messages(list<Message>(1, Message(Message::error, lc::translate("An exception occurred while loading this plugin. Details:").str() + " " + e.what())));
it->second = p;
}
vector<string>& pluginGroup = pluginGroups[threads.size()];
threads.push_back(thread([&]() {
for (auto pluginName : pluginGroup) {
BOOST_LOG_TRIVIAL(trace) << "Loading " << pluginName;
addPlugin(Plugin(*this, pluginName, headersOnly));
}
}));
}
@@ -187,6 +208,11 @@ namespace loot {
return _pluginsFullyLoaded;
}
void Game::addPlugin(const Plugin&& plugin) {
std::lock_guard<std::mutex> lock(mutex);
plugins.emplace(boost::locale::to_lower(plugin.Name()), plugin);
}
std::list<Game> ToGames(const std::list<GameSettings>& settings) {
return list<Game>(settings.begin(), settings.end());
}
+6
View File
@@ -43,8 +43,11 @@ namespace loot {
//Game functions.
Game(); //Sets game to LOOT_Game::autodetect, with all other vars being empty.
Game(const GameSettings& gameSettings);
Game(const Game& game);
Game(const unsigned int baseGameCode, const std::string& lootFolder = "");
Game& operator= (const Game& game);
void Init(bool createFolder, const boost::filesystem::path& gameLocalAppData = "");
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
@@ -58,6 +61,9 @@ namespace loot {
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
private:
bool _pluginsFullyLoaded;
std::mutex mutex;
void addPlugin(const Plugin&& plugin);
};
std::list<Game> ToGames(const std::list<GameSettings>& settings);
-10
View File
@@ -37,16 +37,6 @@ using namespace std;
using libespm::FormId;
namespace loot {
// TODO: Remove the name-only constructor.
Plugin::Plugin(const std::string& n) :
PluginMetadata(n),
libespm::Plugin(libespm::GameId::SKYRIM),
_isEmpty(true),
_isActive(false),
_loadsBsa(false),
crc(0),
numOverrideRecords(0) {}
Plugin::Plugin(const Game& game, const std::string& name, const bool headerOnly) :
PluginMetadata(name),
libespm::Plugin(game.LibespmId()),
-1
View File
@@ -41,7 +41,6 @@ namespace loot {
class Plugin : public PluginMetadata, private libespm::Plugin {
public:
Plugin(const std::string& name);
Plugin(const Game& game, const std::string& name, const bool headerOnly);
using libespm::Plugin::getDescription;
+33 -74
View File
@@ -28,23 +28,21 @@ along with LOOT. If not, see
#include "backend/plugin/plugin.h"
#include "tests/fixtures.h"
class Plugin : public SkyrimTest {};
class Plugin : public SkyrimTest {
protected:
inline virtual void SetUp() {
SkyrimTest::SetUp();
game = loot::Game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
}
loot::Game game;
};
TEST_F(Plugin, ConstructorsAndDataAccess) {
loot::Plugin plugin("Blank.esm");
EXPECT_EQ("Blank.esm", plugin.Name());
EXPECT_TRUE(plugin.getFormIds().empty());
EXPECT_TRUE(plugin.getMasters().empty());
EXPECT_FALSE(plugin.isMasterFile());
EXPECT_TRUE(plugin.IsEmpty());
EXPECT_EQ("", plugin.getDescription());
EXPECT_EQ(0, plugin.Crc());
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
plugin = loot::Plugin(game, "Blank.esm", true);
loot::Plugin plugin(game, "Blank.esm", true);
EXPECT_EQ("Blank.esm", plugin.Name());
EXPECT_TRUE(plugin.getFormIds().empty());
EXPECT_TRUE(plugin.getMasters().empty());
@@ -91,31 +89,18 @@ TEST_F(Plugin, ConstructorsAndDataAccess) {
}
TEST_F(Plugin, LoadsBSA) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
EXPECT_FALSE(loot::Plugin(game, "Blank - Different.esm", true).LoadsBSA());
EXPECT_FALSE(loot::Plugin(game, "Blank\\.esm", true).LoadsBSA());
EXPECT_FALSE(loot::Plugin("Blank.esm").LoadsBSA());
EXPECT_TRUE(loot::Plugin(game, "Blank.esm", true).LoadsBSA());
}
TEST_F(Plugin, IsValid) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
EXPECT_TRUE(loot::Plugin::IsValid("Blank.esm", game));
EXPECT_FALSE(loot::Plugin::IsValid("NotAPlugin.esm", game));
EXPECT_FALSE(loot::Plugin::IsValid("EmptyFile.esm", game));
}
TEST_F(Plugin, IsActive) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
loot::Plugin plugin(game, "Blank.esm", true);
EXPECT_TRUE(plugin.IsActive());
@@ -124,76 +109,62 @@ TEST_F(Plugin, IsActive) {
}
TEST_F(Plugin, EqualityOperator) {
loot::Plugin plugin1("Blank.esm");
loot::Plugin plugin2("blank.esm");
loot::Plugin plugin1(game, "Blank.esm", true);
loot::Plugin plugin2(game, "blank.esm", true);
EXPECT_TRUE(plugin1 == plugin2);
EXPECT_TRUE(plugin2 == plugin1);
plugin1 = loot::Plugin("Blank.esm");
plugin2 = loot::Plugin("Blank.esp");
plugin2 = loot::Plugin(game, "Blank.esp", true);
EXPECT_FALSE(plugin1 == plugin2);
EXPECT_FALSE(plugin2 == plugin1);
plugin1 = loot::Plugin("Blank.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin2 = loot::Plugin(game, "Blan.\\.esm", true);
EXPECT_TRUE(plugin1 == plugin2);
EXPECT_TRUE(plugin2 == plugin1);
plugin1 = loot::Plugin("Blan.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan.esm", true);
EXPECT_FALSE(plugin1 == plugin2);
EXPECT_FALSE(plugin2 == plugin1);
plugin1 = loot::Plugin("Blan.\\.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan.\\.esm", true);
EXPECT_TRUE(plugin1 == plugin2);
EXPECT_TRUE(plugin2 == plugin1);
plugin1 = loot::Plugin("Blan(k|p).esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan(k|p).esm", true);
EXPECT_FALSE(plugin1 == plugin2);
EXPECT_FALSE(plugin2 == plugin1);
}
TEST_F(Plugin, InequalityOperator) {
loot::Plugin plugin1("Blank.esm");
loot::Plugin plugin2("blank.esm");
loot::Plugin plugin1(game, "Blank.esm", true);
loot::Plugin plugin2(game, "blank.esm", true);
EXPECT_FALSE(plugin1 != plugin2);
EXPECT_FALSE(plugin2 != plugin1);
plugin1 = loot::Plugin("Blank.esm");
plugin2 = loot::Plugin("Blank.esp");
plugin2 = loot::Plugin(game, "Blank.esp", true);
EXPECT_TRUE(plugin1 != plugin2);
EXPECT_TRUE(plugin2 != plugin1);
plugin1 = loot::Plugin("Blank.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin2 = loot::Plugin(game, "Blan.\\.esm", true);
EXPECT_FALSE(plugin1 != plugin2);
EXPECT_FALSE(plugin2 != plugin1);
plugin1 = loot::Plugin("Blan.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan.esm", true);
EXPECT_TRUE(plugin1 != plugin2);
EXPECT_TRUE(plugin2 != plugin1);
plugin1 = loot::Plugin("Blan.\\.esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan.\\.esm", true);
EXPECT_FALSE(plugin1 != plugin2);
EXPECT_FALSE(plugin2 != plugin1);
plugin1 = loot::Plugin("Blan(k|p).esm");
plugin2 = loot::Plugin("Blan.\\.esm");
plugin1 = loot::Plugin(game, "Blan(k|p).esm", true);
EXPECT_TRUE(plugin1 != plugin2);
EXPECT_TRUE(plugin2 != plugin1);
}
TEST_F(Plugin, DoFormIDsOverlap) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
loot::Plugin plugin1("Blank.esm");
loot::Plugin plugin2("blank.esm");
loot::Plugin plugin1(game, "Blank.esm", true);
loot::Plugin plugin2(game, "blank.esm", true);
EXPECT_FALSE(plugin1.DoFormIDsOverlap(plugin2));
EXPECT_FALSE(plugin2.DoFormIDsOverlap(plugin1));
@@ -214,12 +185,8 @@ TEST_F(Plugin, DoFormIDsOverlap) {
}
TEST_F(Plugin, OverlapFormIDs) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
loot::Plugin plugin1("Blank.esm");
loot::Plugin plugin2("blank.esm");
loot::Plugin plugin1(game, "Blank.esm", true);
loot::Plugin plugin2(game, "blank.esm", true);
EXPECT_TRUE(plugin1.OverlapFormIDs(plugin2).empty());
EXPECT_TRUE(plugin2.OverlapFormIDs(plugin1).empty());
@@ -250,15 +217,7 @@ TEST_F(Plugin, OverlapFormIDs) {
}
TEST_F(Plugin, CheckInstallValidity) {
loot::Game game(loot::Game::tes5);
game.SetGamePath(dataPath.parent_path());
ASSERT_NO_THROW(game.Init(false, localPath));
loot::Plugin plugin("Blank.esm");
EXPECT_FALSE(plugin.CheckInstallValidity(game));
EXPECT_TRUE(plugin.Messages().empty());
plugin = loot::Plugin(game, "Blank.esm", false);
loot::Plugin plugin(game, "Blank.esm", false);
plugin.Reqs({
loot::File("Blank.missing.esm"),
loot::File("Blank.esp"),
@@ -286,7 +245,7 @@ TEST_F(Plugin, CheckInstallValidity) {
loot::Message(loot::Message::error, "This plugin requires \"Blank - Different.esm\" to be active, but it is inactive."),
}), plugin.Messages());
plugin = loot::Plugin("Blank - Different Master Dependent.esp");
plugin = loot::Plugin(game, "Blank - Different Master Dependent.esp", false);
plugin.Tags({loot::Tag("Filter")});
EXPECT_FALSE(plugin.CheckInstallValidity(game));
EXPECT_TRUE(plugin.Messages().empty());