Cache plugins only after resolving record IDs

This means that if there's an issue while doing that you won't be left with half-loaded plugins.
This commit is contained in:
Oliver Hamlet
2025-03-02 22:40:47 +00:00
parent ccd4e41a99
commit 49fc7910ea
4 changed files with 54 additions and 39 deletions
+10 -4
View File
@@ -264,6 +264,7 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
}
std::mutex mutex;
std::vector<Plugin> plugins;
std::for_each(
std::execution::par_unseq,
pluginPaths.begin(),
@@ -273,10 +274,12 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
const auto resolvedPluginPath =
ResolvePluginPath(GetType(), DataPath(), pluginPath);
auto plugin =
Plugin(GetType(), cache_, resolvedPluginPath, loadHeadersOnly);
std::lock_guard<std::mutex> lock(mutex);
cache_.AddPlugin(
Plugin(GetType(), cache_, resolvedPluginPath, loadHeadersOnly));
plugins.push_back(std::move(plugin));
} catch (const std::exception& e) {
if (logger) {
logger->error(
@@ -290,13 +293,16 @@ void Game::LoadPlugins(const std::vector<std::filesystem::path>& pluginPaths,
if (!loadHeadersOnly &&
(GetType() == GameType::tes3 || GetType() == GameType::openmw ||
GetType() == GameType::starfield)) {
auto plugins = cache_.GetPlugins();
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
for (auto& plugin : plugins) {
plugin->ResolveRecordIds(pluginsMetadata.get());
plugin.ResolveRecordIds(pluginsMetadata.get());
}
}
for (auto& plugin : plugins) {
cache_.AddPlugin(std::move(plugin));
}
conditionEvaluator_->RefreshLoadedPluginsState(GetLoadedPlugins());
}
+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<const Plugin*>& plugins) {
Plugin::GetPluginsMetadata(const std::vector<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<const 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<const Plugin*>& plugins);
GetPluginsMetadata(const std::vector<Plugin>& plugins);
private:
void Load(const std::filesystem::path& path,
+40 -31
View File
@@ -286,19 +286,21 @@ TEST_P(PluginTest, loadingWholePluginShouldReadFields) {
game_.GetType(), game_.GetCache(), game_.DataPath() / pluginName, false);
if (GetParam() == GameType::tes3 || GetParam() == GameType::openmw) {
Plugin master(
game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false);
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master});
std::vector<Plugin> masters;
masters.push_back(Plugin(
game_.GetType(), game_.GetCache(), game_.DataPath() / blankEsm, false));
const auto pluginsMetadata = Plugin::GetPluginsMetadata(masters);
EXPECT_NO_THROW(plugin.ResolveRecordIds(pluginsMetadata.get()));
EXPECT_EQ(4, plugin.GetOverrideRecordCount());
} else if (GetParam() == GameType::starfield) {
Plugin master(game_.GetType(),
game_.GetCache(),
game_.DataPath() / blankFullEsm,
true);
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&master});
std::vector<Plugin> masters;
masters.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / blankFullEsm,
true));
const auto pluginsMetadata = Plugin::GetPluginsMetadata(masters);
EXPECT_NO_THROW(plugin.ResolveRecordIds(pluginsMetadata.get()));
@@ -629,25 +631,27 @@ TEST_P(
? blankMasterDependentEsp
: blankDifferentPluginDependentEsp;
Plugin plugin1(game_.GetType(),
game_.GetCache(),
game_.DataPath() / sourcePluginName,
false);
Plugin plugin2(game_.GetType(),
game_.GetCache(),
game_.DataPath() / updatePluginName,
false);
std::vector<Plugin> plugins;
plugins.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / sourcePluginName,
false));
plugins.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / updatePluginName,
false));
if (GetParam() == GameType::starfield) {
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1});
plugin2.ResolveRecordIds(pluginsMetadata.get());
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
plugins[1].ResolveRecordIds(pluginsMetadata.get());
plugin1.ResolveRecordIds(nullptr);
plugin2.ResolveRecordIds(nullptr);
plugins[0].ResolveRecordIds(nullptr);
plugins[1].ResolveRecordIds(nullptr);
}
EXPECT_FALSE(plugin1.IsValidAsUpdatePlugin());
EXPECT_EQ(GetParam() == GameType::starfield, plugin2.IsValidAsUpdatePlugin());
EXPECT_FALSE(plugins[0].IsValidAsUpdatePlugin());
EXPECT_EQ(GetParam() == GameType::starfield,
plugins[1].IsValidAsUpdatePlugin());
}
TEST_P(PluginTest,
@@ -699,20 +703,25 @@ TEST_P(PluginTest,
? blankMasterDependentEsm
: blankMasterDependentEsm + ".ghost";
Plugin plugin1(
game_.GetType(), game_.GetCache(), game_.DataPath() / plugin1Name, false);
Plugin plugin2(
game_.GetType(), game_.GetCache(), game_.DataPath() / plugin2Name, false);
std::vector<Plugin> plugins;
plugins.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / plugin1Name,
false));
plugins.push_back(Plugin(game_.GetType(),
game_.GetCache(),
game_.DataPath() / plugin2Name,
false));
if (GetParam() == GameType::starfield) {
plugin1.ResolveRecordIds(nullptr);
plugins[0].ResolveRecordIds(nullptr);
const auto pluginsMetadata = Plugin::GetPluginsMetadata({&plugin1});
plugin2.ResolveRecordIds(pluginsMetadata.get());
const auto pluginsMetadata = Plugin::GetPluginsMetadata(plugins);
plugins[1].ResolveRecordIds(pluginsMetadata.get());
}
EXPECT_TRUE(plugin1.DoRecordsOverlap(plugin2));
EXPECT_TRUE(plugin2.DoRecordsOverlap(plugin1));
EXPECT_TRUE(plugins[0].DoRecordsOverlap(plugins[1]));
EXPECT_TRUE(plugins[1].DoRecordsOverlap(plugins[0]));
}
TEST_P(PluginTest,