mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
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:
+51
-25
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user