mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Implemented cache use on game change.
Part of #135. When changing games to a game that has already been loaded, LOOT will now update the plugin headers and load order, and merge that data with any cached data.
This commit is contained in:
@@ -253,27 +253,23 @@ function updateSelectedGame() {
|
||||
}
|
||||
function changeGame(evt) {
|
||||
/* First store current game info in loot.games object.
|
||||
This object may not exist, so initialise it using the corresponding
|
||||
loot.settings.games object. */
|
||||
This object may not exist, so initialise it if not. */
|
||||
var index = undefined;
|
||||
if (loot.games) {
|
||||
for (var i = 0; i < loot.games.length; ++i) {
|
||||
if (loot.games[i].folder == loot.game.folder) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loot.games = [];
|
||||
}
|
||||
if (index == undefined) {
|
||||
for (var i = 0; i < loot.settings.games.length; ++i) {
|
||||
if (loot.settings.games[i].folder == loot.game.folder) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
loot.games = [];
|
||||
loot.games.push(loot.settings.games[index]);
|
||||
index = 0;
|
||||
|
||||
loot.games.push({
|
||||
folder: loot.game.folder
|
||||
});
|
||||
index = loot.games.length - 1;
|
||||
}
|
||||
loot.games[index].globalMessages = loot.game.globalMessages;
|
||||
loot.games[index].masterlist = loot.game.masterlist;
|
||||
@@ -321,8 +317,67 @@ function changeGame(evt) {
|
||||
globalMessages.removeChild(globalMessages.firstElementChild);
|
||||
}
|
||||
|
||||
/* Parse the data sent from C++. */
|
||||
try {
|
||||
var gameInfo = JSON.parse(result, jsonToPlugin);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('getGameData response: ' + result);
|
||||
}
|
||||
|
||||
/* This may not be the first time loading this game this instance of
|
||||
LOOT. Restore cached data if it exists. */
|
||||
index = undefined;
|
||||
if (loot.games) {
|
||||
for (var i = 0; i < loot.games.length; ++i) {
|
||||
if (loot.games[i].folder == gameInfo.folder) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index == undefined) {
|
||||
/* Game data not cached, simply set what was sent. */
|
||||
loot.game = gameInfo;
|
||||
} else {
|
||||
/* Game data cache exists. */
|
||||
loot.game = loot.games[i];
|
||||
/* Now overwrite plugin data with the newly sent data. Also update
|
||||
card and li vars as they were unset when the game was switched
|
||||
from before. */
|
||||
gameInfo.plugins.forEach(function(plugin){
|
||||
var foundPlugin = false;
|
||||
for (var i = 0; i < loot.game.plugins.length; ++i) {
|
||||
if (loot.game.plugins[i].name == plugin.name) {
|
||||
|
||||
loot.game.plugins[i].isActive = plugin.isActive;
|
||||
loot.game.plugins[i].isDummy = plugin.isDummy;
|
||||
loot.game.plugins[i].loadsBSA = plugin.loadsBSA;
|
||||
loot.game.plugins[i].crc = plugin.crc;
|
||||
loot.game.plugins[i].version = plugin.version;
|
||||
|
||||
loot.game.plugins[i].modPriority = plugin.modPriority;
|
||||
loot.game.plugins[i].isGlobalPriority = plugin.isGlobalPriority;
|
||||
loot.game.plugins[i].messages = plugin.messages;
|
||||
loot.game.plugins[i].tags = plugin.tags;
|
||||
loot.game.plugins[i].isDirty = plugin.isDirty;
|
||||
|
||||
loot.game.plugins[i].card = plugin.card;
|
||||
loot.game.plugins[i].li = plugin.li;
|
||||
|
||||
foundPlugin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundPlugin) {
|
||||
/* A new plugin. */
|
||||
loot.game.plugins.push(plugin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Now update interface for new data. */
|
||||
updateInterfaceWithGameInfo(result);
|
||||
updateInterfaceWithGameInfo();
|
||||
|
||||
/* Now update game menu to highlight the newly selected game. */
|
||||
updateSelectedGame();
|
||||
@@ -987,7 +1042,13 @@ function initVars() {
|
||||
}
|
||||
|
||||
if (results.length > 3) {
|
||||
updateInterfaceWithGameInfo(results[3]);
|
||||
try {
|
||||
loot.game = JSON.parse(results[3], jsonToPlugin);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('getGameData response: ' + results[3]);
|
||||
}
|
||||
updateInterfaceWithGameInfo();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1020,14 +1081,7 @@ function masterlistObserver(changes) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateInterfaceWithGameInfo(response) {
|
||||
|
||||
try {
|
||||
loot.game = JSON.parse(response, jsonToPlugin);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log('getGameData response: ' + response);
|
||||
}
|
||||
function updateInterfaceWithGameInfo() {
|
||||
|
||||
var totalMessageNo = 0;
|
||||
var warnMessageNo = 0;
|
||||
|
||||
@@ -722,6 +722,17 @@ namespace loot {
|
||||
group.join_all();
|
||||
}
|
||||
|
||||
bool Game::HasBeenLoaded() {
|
||||
// Easy way to check is by checking the game's master file,
|
||||
// which definitely shouldn't be empty.
|
||||
auto pairIt = plugins.find(_masterFile);
|
||||
|
||||
if (pairIt != plugins.end())
|
||||
return pairIt->second.FormIDs().size() > 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Game::CreateLOOTGameFolder() {
|
||||
//Make sure that the LOOT game path exists.
|
||||
try {
|
||||
|
||||
@@ -123,6 +123,7 @@ namespace loot {
|
||||
void RefreshActivePluginsList();
|
||||
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
|
||||
void LoadPlugins(bool headersOnly); //Loads all installed plugins.
|
||||
bool HasBeenLoaded(); // Checks if the game's plugins have already been loaded.
|
||||
|
||||
std::list<Plugin> Sort(const unsigned int language, std::function<void(const std::string&)> progressCallback);
|
||||
|
||||
|
||||
+94
-81
@@ -356,8 +356,8 @@ namespace loot {
|
||||
auto pluginIt = g_app_state.CurrentGame().plugins.find(boost::locale::to_lower(pluginName));
|
||||
|
||||
// Checking for FormID overlap will only work if the plugins have been loaded, so check if
|
||||
// the first plugin has any FormIDs in memory, and if not load all plugins.
|
||||
if (g_app_state.CurrentGame().plugins.begin()->second.FormIDs().size() == 0)
|
||||
// the plugins have been fully loaded, and if not load all plugins.
|
||||
if (!g_app_state.CurrentGame().HasBeenLoaded())
|
||||
g_app_state.CurrentGame().LoadPlugins(false);
|
||||
|
||||
YAML::Node node;
|
||||
@@ -569,9 +569,16 @@ namespace loot {
|
||||
}
|
||||
|
||||
std::string Handler::GetGameData() {
|
||||
/* GetGameData() can be called for initialising the UI for a game for the first time
|
||||
in a session, or it can be called when changing to a game that has previously been
|
||||
active. In the first case, all data should be loaded, but in the second, only load
|
||||
order and plugin header info should be re-loaded.
|
||||
Determine which case it is by checking to see if the game's plugins object is empty.
|
||||
*/
|
||||
BOOST_LOG_TRIVIAL(info) << "Getting data specific to LOOT's active game.";
|
||||
// Get masterlist revision info and parse if it exists. Also get plugin headers info and parse userlist if it exists.
|
||||
|
||||
bool isFirstLoad = g_app_state.CurrentGame().plugins.empty();
|
||||
g_app_state.CurrentGame().LoadPlugins(true);
|
||||
|
||||
//Sort plugins into their load order.
|
||||
@@ -585,48 +592,44 @@ namespace loot {
|
||||
installed.push_back(pos->second);
|
||||
}
|
||||
|
||||
//Parse masterlist, don't update it.
|
||||
if (fs::exists(g_app_state.CurrentGame().MasterlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Parsing masterlist.";
|
||||
try {
|
||||
g_app_state.CurrentGame().masterlist.MetadataList::Load(g_app_state.CurrentGame().MasterlistPath());
|
||||
if (isFirstLoad) {
|
||||
//Parse masterlist, don't update it.
|
||||
if (fs::exists(g_app_state.CurrentGame().MasterlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Parsing masterlist.";
|
||||
try {
|
||||
g_app_state.CurrentGame().masterlist.MetadataList::Load(g_app_state.CurrentGame().MasterlistPath());
|
||||
}
|
||||
catch (exception &e) {
|
||||
g_app_state.CurrentGame().masterlist.messages.push_back(Message(Message::error, string("An error occurred while parsing the masterlist: ") + e.what()));
|
||||
}
|
||||
}
|
||||
catch (exception &e) {
|
||||
g_app_state.CurrentGame().masterlist.messages.push_back(Message(Message::error, string("An error occurred while parsing the masterlist: ") + e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
//Parse userlist.
|
||||
if (fs::exists(g_app_state.CurrentGame().UserlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Parsing userlist.";
|
||||
try {
|
||||
g_app_state.CurrentGame().userlist.Load(g_app_state.CurrentGame().UserlistPath());
|
||||
}
|
||||
catch (exception &e) {
|
||||
g_app_state.CurrentGame().userlist.messages.push_back(Message(Message::error, string("An error occurred while parsing the userlist: ") + e.what()));
|
||||
//Parse userlist.
|
||||
if (fs::exists(g_app_state.CurrentGame().UserlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Parsing userlist.";
|
||||
try {
|
||||
g_app_state.CurrentGame().userlist.Load(g_app_state.CurrentGame().UserlistPath());
|
||||
}
|
||||
catch (exception &e) {
|
||||
g_app_state.CurrentGame().userlist.messages.push_back(Message(Message::error, string("An error occurred while parsing the userlist: ") + e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now convert to a single object that can be turned into a JSON string
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
//Set language.
|
||||
unsigned int language;
|
||||
if (g_app_state.GetSettings()["language"])
|
||||
language = Language(g_app_state.GetSettings()["language"].as<string>()).Code();
|
||||
else
|
||||
language = Language::any;
|
||||
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
|
||||
|
||||
// The data structure is to be set as 'loot.game'.
|
||||
YAML::Node gameNode;
|
||||
|
||||
// ID the game using its folder value.
|
||||
gameNode["folder"] = g_app_state.CurrentGame().FolderName();
|
||||
|
||||
// Store the masterlist revision and date.
|
||||
gameNode["masterlist"]["revision"] = g_app_state.CurrentGame().masterlist.GetRevision(g_app_state.CurrentGame().MasterlistPath());
|
||||
gameNode["masterlist"]["date"] = g_app_state.CurrentGame().masterlist.GetDate(g_app_state.CurrentGame().MasterlistPath());
|
||||
if (isFirstLoad) {
|
||||
// Store the masterlist revision and date.
|
||||
gameNode["masterlist"]["revision"] = g_app_state.CurrentGame().masterlist.GetRevision(g_app_state.CurrentGame().MasterlistPath());
|
||||
gameNode["masterlist"]["date"] = g_app_state.CurrentGame().masterlist.GetDate(g_app_state.CurrentGame().MasterlistPath());
|
||||
}
|
||||
|
||||
// Now store plugin data.
|
||||
for (const auto& plugin : installed) {
|
||||
@@ -634,6 +637,16 @@ namespace loot {
|
||||
the editor, and also processed data for the main display.
|
||||
*/
|
||||
YAML::Node pluginNode;
|
||||
// Find the masterlist metadata for this plugin. Treat Bash Tags from the plugin
|
||||
// description as part of it.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting masterlist metadata for: " << plugin.Name();
|
||||
Plugin mlistPlugin(plugin);
|
||||
mlistPlugin.MergeMetadata(g_app_state.CurrentGame().masterlist.FindPlugin(plugin.Name()));
|
||||
|
||||
// Now do the same again for any userlist data.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting userlist metadata for: " << plugin.Name();
|
||||
Plugin ulistPlugin(g_app_state.CurrentGame().userlist.FindPlugin(plugin.Name()));
|
||||
|
||||
pluginNode["__type"] = "Plugin"; // For conversion back into a JS typed object.
|
||||
pluginNode["name"] = plugin.Name();
|
||||
pluginNode["isActive"] = g_app_state.CurrentGame().IsActive(plugin.Name());
|
||||
@@ -642,37 +655,27 @@ namespace loot {
|
||||
pluginNode["crc"] = IntToHexString(plugin.Crc());
|
||||
pluginNode["version"] = plugin.Version();
|
||||
|
||||
// Find the masterlist metadata for this plugin.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting masterlist metadata for: " << plugin.Name();
|
||||
Plugin mlistPlugin(plugin);
|
||||
mlistPlugin.MergeMetadata(g_app_state.CurrentGame().masterlist.FindPlugin(plugin.Name()));
|
||||
if (isFirstLoad) {
|
||||
if (!mlistPlugin.HasNameOnly()) {
|
||||
// Now add the masterlist metadata to the pluginNode.
|
||||
pluginNode["masterlist"]["after"] = mlistPlugin.LoadAfter();
|
||||
pluginNode["masterlist"]["req"] = mlistPlugin.Reqs();
|
||||
pluginNode["masterlist"]["inc"] = mlistPlugin.Incs();
|
||||
pluginNode["masterlist"]["msg"] = mlistPlugin.Messages();
|
||||
pluginNode["masterlist"]["tag"] = mlistPlugin.Tags();
|
||||
pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo();
|
||||
}
|
||||
|
||||
if (!mlistPlugin.HasNameOnly()) {
|
||||
// Now add the masterlist metadata to the pluginNode.
|
||||
pluginNode["masterlist"]["after"] = mlistPlugin.LoadAfter();
|
||||
pluginNode["masterlist"]["req"] = mlistPlugin.Reqs();
|
||||
pluginNode["masterlist"]["inc"] = mlistPlugin.Incs();
|
||||
pluginNode["masterlist"]["msg"] = mlistPlugin.Messages();
|
||||
pluginNode["masterlist"]["tag"] = mlistPlugin.Tags();
|
||||
pluginNode["masterlist"]["dirty"] = mlistPlugin.DirtyInfo();
|
||||
}
|
||||
|
||||
// Now do the same again for any userlist data.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Getting userlist metadata for: " << plugin.Name();
|
||||
Plugin ulistPlugin(plugin);
|
||||
// Clear Bash Tags to prevent false positives.
|
||||
ulistPlugin.Tags(set<Tag>());
|
||||
ulistPlugin.MergeMetadata(g_app_state.CurrentGame().userlist.FindPlugin(plugin.Name()));
|
||||
|
||||
if (!ulistPlugin.HasNameOnly()) {
|
||||
// Now add the userlist metadata to the pluginNode.
|
||||
pluginNode["userlist"]["enabled"] = ulistPlugin.Enabled();
|
||||
pluginNode["userlist"]["after"] = ulistPlugin.LoadAfter();
|
||||
pluginNode["userlist"]["req"] = ulistPlugin.Reqs();
|
||||
pluginNode["userlist"]["inc"] = ulistPlugin.Incs();
|
||||
pluginNode["userlist"]["msg"] = ulistPlugin.Messages();
|
||||
pluginNode["userlist"]["tag"] = ulistPlugin.Tags();
|
||||
pluginNode["userlist"]["dirty"] = ulistPlugin.DirtyInfo();
|
||||
if (!ulistPlugin.HasNameOnly()) {
|
||||
// Now add the userlist metadata to the pluginNode.
|
||||
pluginNode["userlist"]["enabled"] = ulistPlugin.Enabled();
|
||||
pluginNode["userlist"]["after"] = ulistPlugin.LoadAfter();
|
||||
pluginNode["userlist"]["req"] = ulistPlugin.Reqs();
|
||||
pluginNode["userlist"]["inc"] = ulistPlugin.Incs();
|
||||
pluginNode["userlist"]["msg"] = ulistPlugin.Messages();
|
||||
pluginNode["userlist"]["tag"] = ulistPlugin.Tags();
|
||||
pluginNode["userlist"]["dirty"] = ulistPlugin.DirtyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// Now merge masterlist and userlist metadata and evaluate,
|
||||
@@ -687,26 +690,36 @@ namespace loot {
|
||||
gameNode["plugins"].push_back(pluginNode);
|
||||
}
|
||||
|
||||
//Evaluate any conditions in the global messages.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
|
||||
list<Message> messages = g_app_state.CurrentGame().masterlist.messages;
|
||||
messages.insert(messages.end(), g_app_state.CurrentGame().userlist.messages.begin(), g_app_state.CurrentGame().userlist.messages.end());
|
||||
try {
|
||||
list<Message>::iterator it = messages.begin();
|
||||
while (it != messages.end()) {
|
||||
if (!it->EvalCondition(g_app_state.CurrentGame(), language))
|
||||
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(Message(Message::error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
if (isFirstLoad) {
|
||||
//Set language.
|
||||
unsigned int language;
|
||||
if (g_app_state.GetSettings()["language"])
|
||||
language = Language(g_app_state.GetSettings()["language"].as<string>()).Code();
|
||||
else
|
||||
language = Language::any;
|
||||
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
|
||||
|
||||
// Now store global messages from masterlist.
|
||||
gameNode["globalMessages"] = messages;
|
||||
//Evaluate any conditions in the global messages.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
|
||||
list<Message> messages = g_app_state.CurrentGame().masterlist.messages;
|
||||
messages.insert(messages.end(), g_app_state.CurrentGame().userlist.messages.begin(), g_app_state.CurrentGame().userlist.messages.end());
|
||||
try {
|
||||
list<Message>::iterator it = messages.begin();
|
||||
while (it != messages.end()) {
|
||||
if (!it->EvalCondition(g_app_state.CurrentGame(), language))
|
||||
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(Message(Message::error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
|
||||
// Now store global messages from masterlist.
|
||||
gameNode["globalMessages"] = messages;
|
||||
}
|
||||
|
||||
return JSON::stringify(gameNode);
|
||||
}
|
||||
@@ -835,8 +848,8 @@ namespace loot {
|
||||
language = Language::any;
|
||||
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
|
||||
|
||||
// Check if the first plugin has any FormIDs in memory, and if not load all plugins.
|
||||
if (g_app_state.CurrentGame().plugins.begin()->second.FormIDs().size() == 0)
|
||||
// Check if the plugins have been fully loaded, and if not load all plugins.
|
||||
if (!g_app_state.CurrentGame().HasBeenLoaded())
|
||||
g_app_state.CurrentGame().LoadPlugins(false);
|
||||
|
||||
//Sort plugins into their load order.
|
||||
|
||||
Reference in New Issue
Block a user