Fix LoadPlugins() with missing already-loaded plugins

This commit is contained in:
Oliver Hamlet
2025-03-15 20:54:10 +00:00
parent b7f47af3fd
commit e4fa201dd8
7 changed files with 81 additions and 26 deletions
+7 -7
View File
@@ -293,7 +293,9 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
if (!loadHeadersOnly &&
(GetType() == GameType::tes3 || GetType() == GameType::openmw ||
GetType() == GameType::starfield)) {
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
const auto loadedPlugins = cache_.GetPluginsWithReplacements(plugins);
const auto pluginsMetadata = Plugin::GetPluginsMetadata(loadedPlugins);
for (auto& plugin : plugins) {
plugin.ResolveRecordIds(pluginsMetadata.get());
}
@@ -306,9 +308,7 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
conditionEvaluator_->RefreshLoadedPluginsState(GetLoadedPlugins());
}
void Game::ClearLoadedPlugins() {
cache_.ClearCachedPlugins();
}
void Game::ClearLoadedPlugins() { cache_.ClearCachedPlugins(); }
const PluginInterface* Game::GetPlugin(const std::string& pluginName) const {
return cache_.GetPlugin(pluginName);
@@ -348,9 +348,9 @@ std::vector<std::string> Game::SortPlugins(
const auto newLoadOrder =
loot::SortPlugins(std::move(pluginsSortingData),
database_.GetGroups(false),
database_.GetUserGroups(),
loadOrderHandler_.GetEarlyLoadingPlugins());
database_.GetGroups(false),
database_.GetUserGroups(),
loadOrderHandler_.GetEarlyLoadingPlugins());
if (logger) {
logger->debug("Calculated order:");
+19 -3
View File
@@ -56,6 +56,24 @@ void GameCache::AddPlugin(Plugin&& plugin) {
}
}
std::vector<const Plugin*> GameCache::GetPluginsWithReplacements(
const std::vector<Plugin>& newPlugins) const {
std::unordered_map<std::string, const Plugin*> pluginsMap;
for (const auto& plugin : newPlugins) {
pluginsMap.emplace(NormalizeFilename(plugin.GetName()), &plugin);
}
for (const auto& [key, plugin] : plugins_) {
pluginsMap.emplace(key, plugin.get());
}
std::vector<const Plugin*> loadedPlugins;
for (const auto& [key, plugin] : pluginsMap) {
loadedPlugins.push_back(plugin);
}
return loadedPlugins;
}
std::set<std::filesystem::path> GameCache::GetArchivePaths() const {
return archivePaths_;
}
@@ -64,7 +82,5 @@ void GameCache::CacheArchivePaths(std::set<std::filesystem::path>&& paths) {
archivePaths_ = std::move(paths);
}
void GameCache::ClearCachedPlugins() {
plugins_.clear();
}
void GameCache::ClearCachedPlugins() { plugins_.clear(); }
}
+3
View File
@@ -37,6 +37,9 @@ public:
const Plugin* GetPlugin(const std::string& pluginName) const;
void AddPlugin(Plugin&& plugin);
std::vector<const Plugin*> GetPluginsWithReplacements(
const std::vector<Plugin>& newPlugins) const;
std::set<std::filesystem::path> GetArchivePaths() const;
void CacheArchivePaths(std::set<std::filesystem::path>&& paths);
+3 -3
View File
@@ -593,7 +593,7 @@ std::string Plugin::GetDescription() const {
}
std::unique_ptr<Vec_PluginMetadata, decltype(&esp_plugins_metadata_free)>
Plugin::GetPluginsMetadata(const std::vector<Plugin>& plugins) {
Plugin::GetPluginsMetadata(const std::vector<const Plugin*>& plugins) {
if (plugins.empty()) {
return std::unique_ptr<Vec_PluginMetadata,
decltype(&esp_plugins_metadata_free)>(
@@ -603,9 +603,9 @@ Plugin::GetPluginsMetadata(const std::vector<Plugin>& plugins) {
std::vector<const ::Plugin*> esPlugins;
esPlugins.reserve(plugins.size());
for (const auto& plugin : plugins) {
const auto esPlugin = plugin.esPlugin.get();
const auto esPlugin = plugin->esPlugin.get();
if (esPlugin != nullptr) {
esPlugins.push_back(plugin.esPlugin.get());
esPlugins.push_back(plugin->esPlugin.get());
}
}
+1 -1
View File
@@ -91,7 +91,7 @@ public:
static std::unique_ptr<Vec_PluginMetadata,
decltype(&esp_plugins_metadata_free)>
GetPluginsMetadata(const std::vector<Plugin>& plugins);
GetPluginsMetadata(const std::vector<const Plugin*>& plugins);
private:
void Load(const std::filesystem::path& path,
+36
View File
@@ -445,6 +445,42 @@ TEST_P(
}
}
TEST_P(
GameTest,
loadPluginsShouldThrowIfAPluginHasAMasterThatIsNotInTheInputAndIsNotAlreadyLoadedAndGameIsMorrowindOrStarfield) {
Game game = Game(GetParam(), gamePath, localPath);
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw ||
GetParam() == GameType::starfield) {
try {
game.LoadPlugins({blankMasterDependentEsm}, false);
FAIL();
} catch (const std::system_error& e) {
EXPECT_EQ(ESP_ERROR_PLUGIN_METADATA_NOT_FOUND, e.code().value());
EXPECT_EQ(esplugin_category(), e.code().category());
}
} else {
game.LoadPlugins({blankMasterDependentEsm}, false);
EXPECT_NE(nullptr, game.GetPlugin(blankMasterDependentEsm));
}
}
TEST_P(
GameTest,
loadPluginsShouldNotThrowIfAPluginHasAMasterThatIsNotInTheInputButIsAlreadyLoaded) {
Game game = Game(GetParam(), gamePath, localPath);
const auto pluginName =
GetParam() == GameType::starfield ? blankFullEsm : blankEsm;
game.LoadPlugins({pluginName}, true);
game.LoadPlugins({blankMasterDependentEsm}, false);
EXPECT_NE(nullptr, game.GetPlugin(blankMasterDependentEsm));
}
TEST_P(GameTest, sortPluginsWithNoLoadedPluginsShouldReturnAnEmptyList) {
Game game = Game(GetParam(), gamePath, localPath);
+12 -12
View File
@@ -353,21 +353,19 @@ TEST_P(PluginTest, loadingWholePluginShouldReadFields) {
game_.GetType(), game_.GetCache(), game_.DataPath() / pluginName, false);
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw) {
std::vector<Plugin> masters;
masters.push_back(Plugin(
game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false));
const auto pluginsMetadata = Plugin::GetPluginsMetadata(masters);
Plugin master(
game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false);
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master});
EXPECT_NO_THROW(plugin.ResolveRecordIds(pluginsMetadata.get()));
EXPECT_EQ(4, plugin.GetOverrideRecordCount());
} else if (GetParam() == GameType::starfield) {
std::vector<Plugin> masters;
masters.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / blankFullEsm,
true));
const auto pluginsMetadata = Plugin::GetPluginsMetadata(masters);
Plugin master(game_.GetType(),
game_.GetCache(),
game_.DataPath() / blankFullEsm,
true);
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master});
EXPECT_NO_THROW(plugin.ResolveRecordIds(pluginsMetadata.get()));
@@ -709,7 +707,8 @@ TEST_P(
false));
if (GetParam() == GameType::starfield) {
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
const auto pluginsMetadata =
Plugin::GetPluginsMetadata({&plugins[0], &plugins[1]});
plugins[1].ResolveRecordIds(pluginsMetadata.get());
plugins[0].ResolveRecordIds(nullptr);
@@ -782,7 +781,8 @@ TEST_P(PluginTest,
if (GetParam() == GameType::starfield) {
plugins[0].ResolveRecordIds(nullptr);
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
const auto pluginsMetadata =
Plugin::GetPluginsMetadata({&plugins[0], &plugins[1]});
plugins[1].ResolveRecordIds(pluginsMetadata.get());
}