Implement the new GameSettings class

* Make LootState inherit from LootSettings
* Refactor ToGames()/ToGameSettings() as static members of LootState

As the conversion functions are no longer accessible, their unit
tests have been removed.
This commit is contained in:
Oliver Hamlet
2016-01-12 10:58:51 +00:00
parent 1754939aab
commit 6307a3c194
8 changed files with 80 additions and 339 deletions
-8
View File
@@ -191,12 +191,4 @@ namespace loot {
return LoadOrderHandler::IsPluginActive(pluginName);
}
}
std::list<Game> ToGames(const std::list<GameSettings>& settings) {
return list<Game>(settings.begin(), settings.end());
}
std::list<GameSettings> ToGameSettings(const std::list<Game>& games) {
return list<GameSettings>(games.begin(), games.end());
}
}
-3
View File
@@ -54,9 +54,6 @@ namespace loot {
private:
bool _pluginsFullyLoaded;
};
std::list<Game> ToGames(const std::list<GameSettings>& settings);
std::list<GameSettings> ToGameSettings(const std::list<Game>& games);
}
#endif
+14 -47
View File
@@ -275,20 +275,16 @@ namespace loot {
else if (requestName == "closeSettings") {
BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object.";
// Update the game details and settings.
_lootState.UpdateSettings(request["args"][0]);
// Update the settings.
_lootState.load(request["args"][0]);
// If the user has deleted a default game, we don't want to restore it now.
// It will be restored when LOOT is next loaded.
try {
BOOST_LOG_TRIVIAL(trace) << "Updating games object.";
list<GameSettings> games(request["args"][0]["games"].as< list<GameSettings> >());
_lootState.UpdateGames(games);
_lootState.UpdateGamesFromSettings();
// Also enable/disable debug logging as required.
if (request["args"][0]["enableDebugLogging"] && request["args"][0]["enableDebugLogging"].as<bool>())
boost::log::core::get()->set_logging_enabled(true);
else
boost::log::core::get()->set_logging_enabled(false);
boost::log::core::get()->set_logging_enabled(_lootState.isDebugLoggingEnabled());
// Now send back the new list of installed games to the UI.
BOOST_LOG_TRIVIAL(trace) << "Getting new list of installed games.";
@@ -381,11 +377,7 @@ namespace loot {
// Has two args: the first is the filter ID, the second is the value.
BOOST_LOG_TRIVIAL(trace) << "Saving filter states.";
try {
YAML::Node settings = _lootState.GetSettings();
settings["filters"][request["args"][0].as<string>()] = request["args"][1];
_lootState.UpdateSettings(settings);
_lootState.storeFilterState(request["args"][0].as<string>(), request["args"][1].as<bool>());
callback->Success("");
}
catch (exception &e) {
@@ -581,7 +573,7 @@ namespace loot {
std::string Handler::GetSettings() {
BOOST_LOG_TRIVIAL(info) << "Getting LOOT settings.";
return JSON::stringify(_lootState.GetSettings());
return JSON::stringify(_lootState.toYaml());
}
std::string Handler::GetLanguages() {
@@ -779,13 +771,7 @@ namespace loot {
}
SendProgressUpdate(frame, loc::translate("Loading general messages..."));
//Set language.
unsigned int language;
if (_lootState.GetSettings()["language"])
language = Language(_lootState.GetSettings()["language"].as<string>()).Code();
else
language = Language::any;
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name();
//Evaluate any conditions in the global messages.
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
@@ -801,7 +787,7 @@ namespace loot {
try {
list<Message>::iterator it = messages.begin();
while (it != messages.end()) {
if (!it->EvalCondition(_lootState.CurrentGame(), language))
if (!it->EvalCondition(_lootState.CurrentGame(), _lootState.getLanguage().Code()))
it = messages.erase(it);
else
++it;
@@ -830,14 +816,7 @@ namespace loot {
void Handler::UpdateMasterlist(CefRefPtr<CefFrame> frame, CefRefPtr<Callback> callback) {
try {
BOOST_LOG_TRIVIAL(debug) << "Updating and parsing masterlist.";
//Set language.
unsigned int language;
if (_lootState.GetSettings()["language"])
language = Language(_lootState.GetSettings()["language"].as<string>()).Code();
else
language = Language::any;
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name();
// Update / parse masterlist.
bool wasChanged = true;
@@ -914,7 +893,7 @@ namespace loot {
try {
list<Message>::iterator it = messages.begin();
while (it != messages.end()) {
if (!it->EvalCondition(_lootState.CurrentGame(), language))
if (!it->EvalCondition(_lootState.CurrentGame(), _lootState.getLanguage().Code()))
it = messages.erase(it);
else
++it;
@@ -974,13 +953,7 @@ namespace loot {
void Handler::SortPlugins(CefRefPtr<CefFrame> frame, CefRefPtr<Callback> callback) {
BOOST_LOG_TRIVIAL(info) << "Beginning sorting operation.";
//Set language.
unsigned int language;
if (_lootState.GetSettings()["language"])
language = Language(_lootState.GetSettings()["language"].as<string>()).Code();
else
language = Language::any;
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name();
try {
// Always reload all the plugins.
@@ -989,7 +962,7 @@ namespace loot {
//Sort plugins into their load order.
PluginSorter sorter;
list<Plugin> plugins = sorter.Sort(_lootState.CurrentGame(), language, [this, frame](const string& message) {
list<Plugin> plugins = sorter.Sort(_lootState.CurrentGame(), _lootState.getLanguage().Code(), [this, frame](const string& message) {
this->SendProgressUpdate(frame, message);
});
@@ -1024,13 +997,7 @@ namespace loot {
}
YAML::Node Handler::GenerateDerivedMetadata(const Plugin& file, const PluginMetadata& masterlist, const PluginMetadata& userlist) {
//Set language.
unsigned int language;
if (_lootState.GetSettings()["language"])
language = Language(_lootState.GetSettings()["language"].as<string>()).Code();
else
language = Language::any;
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name();
// Now rederive the displayed metadata from the masterlist and userlist.
Plugin tempPlugin(file);
@@ -1041,7 +1008,7 @@ namespace loot {
//Evaluate any conditions
BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data.";
try {
tempPlugin.EvalAllConditions(_lootState.CurrentGame(), language);
tempPlugin.EvalAllConditions(_lootState.CurrentGame(), _lootState.getLanguage().Code());
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "\"" << tempPlugin.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what();
+3 -5
View File
@@ -96,15 +96,13 @@ namespace loot {
// Need to set the global locale for this process so that messages will
// be translated.
BOOST_LOG_TRIVIAL(debug) << "Initialising language settings in UI thread.";
const YAML::Node& settings = lootState.GetSettings();
if (settings["language"] && settings["language"].as<string>() != Language(Language::english).Locale()) {
if (lootState.getLanguage().Code() != Language::english) {
boost::locale::generator gen;
gen.add_messages_path(g_path_l10n.string());
gen.add_messages_domain("loot");
loot::Language lang(settings["language"].as<string>());
BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lang.Name();
locale::global(gen(lang.Locale() + ".UTF-8"));
BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lootState.getLanguage().Name();
locale::global(gen(lootState.getLanguage().Locale() + ".UTF-8"));
boost::filesystem::path::imbue(locale());
}
+17 -16
View File
@@ -97,15 +97,13 @@ namespace loot {
#endif
// Set window size & position.
YAML::Node settings = _lootState.GetSettings();
if (settings["window"]["left"] && settings["window"]["top"] && settings["window"]["right"] && settings["window"]["bottom"]) {
if (_lootState.isWindowPositionStored()) {
#ifdef _WIN32
RECT rc;
rc.left = settings["window"]["left"].as<long>();
rc.top = settings["window"]["top"].as<long>();
rc.right = settings["window"]["right"].as<long>();
rc.bottom = settings["window"]["bottom"].as<long>();
rc.left = _lootState.getWindowPosition().left;
rc.top = _lootState.getWindowPosition().top;
rc.right = _lootState.getWindowPosition().right;
rc.bottom = _lootState.getWindowPosition().bottom;
// Fit the saved window size/position to the current monitor setup.
@@ -175,21 +173,24 @@ namespace loot {
void LootHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
assert(CefCurrentlyOn(TID_UI));
// Save window size & position.
YAML::Node settings = _lootState.GetSettings();
#ifdef _WIN32
RECT rc;
GetWindowRect(browser->GetHost()->GetWindowHandle(), &rc);
settings["window"]["left"] = rc.left;
settings["window"]["top"] = rc.top;
settings["window"]["right"] = rc.right;
settings["window"]["bottom"] = rc.bottom;
LootSettings::WindowPosition position;
position.top = rc.top;
position.bottom = rc.bottom;
position.left = rc.left;
position.right = rc.right;
_lootState.storeWindowPosition(position);
#endif
_lootState.UpdateSettings(settings);
_lootState.SaveSettings();
try {
_lootState.save(g_path_settings);
}
catch (std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "Failed to save LOOT's settings. Error: " << e.what();
}
// Cancel any javascript callbacks.
browser_side_router_->OnBeforeClose(browser);
+37 -203
View File
@@ -71,18 +71,12 @@ namespace loot {
}
if (fs::exists(g_path_settings)) {
try {
fs::ifstream in(g_path_settings);
_settings = YAML::Load(in);
in.close();
load(g_path_settings);
}
catch (exception& e) {
_initErrors.push_back((format(translate("Error: Settings parsing failed. %1%")) % e.what()).str());
}
}
// Check if the settings are valid (or if they don't exist).
if (!AreSettingsValid()) {
_settings = GetDefaultSettings();
}
//Set up logging.
boost::log::add_file_log(
@@ -96,14 +90,7 @@ namespace loot {
)
);
boost::log::add_common_attributes();
bool enableDebugLogging = false;
if (_settings["enableDebugLogging"]) {
enableDebugLogging = _settings["enableDebugLogging"].as<bool>();
}
if (enableDebugLogging)
boost::log::core::get()->set_logging_enabled(true);
else
boost::log::core::get()->set_logging_enabled(false);
boost::log::core::get()->set_logging_enabled(isDebugLoggingEnabled());
// Log some useful info.
BOOST_LOG_TRIVIAL(info) << "LOOT Version: " << g_version_major << "." << g_version_minor << "." << g_version_patch;
@@ -121,9 +108,9 @@ namespace loot {
fs::remove(g_path_local / "CEFDebugLog.txt");
// Now that settings have been loaded, set the locale again to handle translations.
if (_settings["language"] && _settings["language"].as<string>() != Language(Language::english).Locale()) {
if (getLanguage().Code() != Language::english) {
BOOST_LOG_TRIVIAL(debug) << "Initialising language settings.";
loot::Language lang(_settings["language"].as<string>());
loot::Language lang(getLanguage());
BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lang.Name();
//Boost.Locale initialisation: Generate and imbue locales.
@@ -136,17 +123,7 @@ namespace loot {
//Detect installed games.
BOOST_LOG_TRIVIAL(debug) << "Detecting installed games.";
try {
_games = ToGames(GetGameSettings(_settings));
}
catch (YAML::Exception& e) {
BOOST_LOG_TRIVIAL(error) << "Games' settings parsing failed. " << e.what();
_initErrors.push_back((format(translate("Error: Games' settings parsing failed. %1%")) % e.what()).str());
// Now redo, but with no games settings, so only the hardcoded defaults get loaded. It means the user can
// at least still then edit them.
YAML::Node node;
_games = ToGames(GetGameSettings(node));
}
_games = ToGames(getGameSettings());
try {
BOOST_LOG_TRIVIAL(debug) << "Selecting game.";
@@ -154,7 +131,7 @@ namespace loot {
BOOST_LOG_TRIVIAL(debug) << "Initialising game-specific settings.";
_currentGame->Init(true);
// Update game path in settings object.
_settings["games"] = ToGameSettings(_games);
storeGameSettings(ToGameSettings(_games));
}
catch (loot::error &e) {
if (e.code() == loot::error::no_game_detected) {
@@ -172,7 +149,13 @@ namespace loot {
return _initErrors;
}
void LootState::UpdateGames(std::list<GameSettings>& games) {
void LootState::save(const boost::filesystem::path & file) {
storeLastGame(_currentGame->FolderName());
updateLastVersion();
LootSettings::save(file);
}
void LootState::UpdateGamesFromSettings() {
// Acquire the lock for the scope of this method.
base::AutoLock lock_scope(_lock);
@@ -180,7 +163,7 @@ namespace loot {
// Update existing games, add new games.
BOOST_LOG_TRIVIAL(trace) << "Updating existing games and adding new games.";
for (auto &game : games) {
for (const auto &game : getGameSettings()) {
auto pos = find(_games.begin(), _games.end(), game);
if (pos != _games.end()) {
@@ -214,7 +197,7 @@ namespace loot {
// Re-initialise the current game in case the game path setting was changed.
_currentGame->Init(true);
// Update game path in settings object.
_settings["games"] = ToGameSettings(_games);
storeGameSettings(ToGameSettings(_games));
}
void LootState::ChangeGame(const std::string& newGameFolder) {
@@ -226,7 +209,7 @@ namespace loot {
_currentGame->Init(true);
// Update game path in settings object.
_settings["games"] = ToGameSettings(_games);
storeGameSettings(ToGameSettings(_games));
BOOST_LOG_TRIVIAL(debug) << "New game is " << _currentGame->Name();
}
@@ -246,186 +229,37 @@ namespace loot {
return installedGames;
}
const YAML::Node& LootState::GetSettings() const {
return _settings;
}
void LootState::UpdateSettings(const YAML::Node& settings) {
// Acquire the lock for the scope of this method.
base::AutoLock lock_scope(_lock);
_settings = settings;
}
void LootState::SaveSettings() {
// Acquire the lock for the scope of this method.
base::AutoLock lock_scope(_lock);
_settings["lastGame"] = _currentGame->FolderName();
_settings["lastVersion"] = to_string(g_version_major) + "." + to_string(g_version_minor) + "." + to_string(g_version_patch);
//Save settings.
try {
BOOST_LOG_TRIVIAL(debug) << "Saving LOOT settings.";
YAML::Emitter yout;
yout.SetIndent(2);
yout << _settings;
fs::ofstream out(loot::g_path_settings);
out << yout.c_str();
out.close();
}
catch (std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "Failed to save LOOT's settings. Error: " << e.what();
}
}
void LootState::SelectGame(std::string preferredGame) {
if (preferredGame.empty()) {
// Get preferred game from settings.
if (_settings["game"] && _settings["game"].as<string>() != "auto")
preferredGame = _settings["game"].as<string>();
else if (_settings["lastGame"] && _settings["lastGame"].as<string>() != "auto")
preferredGame = _settings["lastGame"].as<string>();
if (getGame() != "auto")
preferredGame = getGame();
else if (getLastGame() != "auto")
preferredGame = getLastGame();
}
// Get iterator to preferred game if there is one.
_currentGame = _games.end();
for (auto it = _games.begin(); it != _games.end(); ++it) {
if ((preferredGame.empty() && it->IsInstalled())
|| (!preferredGame.empty() && preferredGame == it->FolderName() && it->IsInstalled())) {
_currentGame = it;
return;
}
// Get iterator to preferred game.
_currentGame = find_if(begin(_games), end(_games), [&](auto& game) {
return (preferredGame.empty() || preferredGame == game.FolderName()) && game.IsInstalled();
});
// If the preferred game cannot be found, get the first installed game.
if (_currentGame == end(_games)) {
_currentGame = find_if(begin(_games), end(_games), [](auto& game) {
return game.IsInstalled();
});
}
// Preferred game not found, just pick the first installed one.
for (auto it = _games.begin(); it != _games.end(); ++it) {
if (it->IsInstalled()) {
_currentGame = it;
return;
}
// If no game can be selected, throw an exception.
if (_currentGame == end(_games)) {
BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected.";
throw error(error::no_game_detected, translate("None of the supported games were detected."));
}
BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected.";
throw error(error::no_game_detected, translate("None of the supported games were detected."));
}
bool LootState::AreSettingsValid() {
// Acquire the lock for the scope of this method.
base::AutoLock lock_scope(_lock);
if (!_settings["language"]) {
if (_settings["Language"]) {
// Conversion from 0.6 key.
_settings["language"] = _settings["Language"];
_settings.remove("Language");
}
else
return false;
}
if (!_settings["game"]) {
if (_settings["Game"]) {
// Conversion from 0.6 key.
_settings["game"] = _settings["Game"];
_settings.remove("Game");
}
else
return false;
}
if (!_settings["lastGame"]) {
if (_settings["Last Game"]) {
// Conversion from 0.6 key.
_settings["lastGame"] = _settings["Last Game"];
_settings.remove("Last Game");
}
else
return false;
}
if (!_settings["enableDebugLogging"]) {
if (_settings["Debug Verbosity"]) {
// Conversion from 0.6 key.
_settings["enableDebugLogging"] = (_settings["Debug Verbosity"].as<unsigned int>() > 0);
_settings.remove("Debug Verbosity");
}
else if (_settings["debugVerbosity"]) {
// Conversion from 0.7 alpha key
_settings["enableDebugLogging"] = (_settings["debugVerbosity"].as<unsigned int>() > 0);
_settings.remove("debugVerbosity");
}
else
return false;
}
if (!_settings["updateMasterlist"]) {
if (_settings["Update Masterlist"]) {
// Conversion from 0.6 key.
_settings["updateMasterlist"] = _settings["Update Masterlist"];
_settings.remove("Update Masterlist");
}
else
return false;
}
if (!_settings["games"]) {
if (_settings["Games"]) {
// Conversion from 0.6 key.
_settings["games"] = _settings["Games"];
for (auto node : _settings["games"]) {
if (node["url"]) {
node["repo"] = node["url"];
node["branch"] = "v0.8";
node.remove("url");
}
}
_settings.remove("Games");
}
else
return false;
}
else {
// Update existing default branches to new version default, if the
// default repositories are used.
for (auto node : _settings["games"]) {
GameSettings settings(node.as<GameSettings>());
set<string> oldDefaultBranches({
"master",
"v0.7",
});
if (settings.RepoURL() == GameSettings(settings.Id()).RepoURL()
&& oldDefaultBranches.count(settings.RepoBranch()) == 1) {
node["branch"] = "v0.8";
}
}
}
if (_settings["windows"])
_settings.remove("windows");
return true;
std::list<Game> LootState::ToGames(const std::vector<GameSettings>& settings) {
return list<Game>(settings.begin(), settings.end());
}
YAML::Node LootState::GetDefaultSettings() const {
YAML::Node root;
root["language"] = "en";
root["game"] = "auto";
root["lastGame"] = "auto";
root["enableDebugLogging"] = false;
root["updateMasterlist"] = true;
// Add base game definitions, and Nehrim.
GetGameSettings(root);
GameSettings settings(GameSettings::tes4, "Nehrim");
settings.SetName("Nehrim - At Fate's Edge")
.SetMaster("Nehrim.esm")
.SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation");
root["games"].push_back(settings);
return root;
std::vector<GameSettings> LootState::ToGameSettings(const std::list<Game>& games) {
return vector<GameSettings>(games.begin(), games.end());
}
}
+9 -13
View File
@@ -25,35 +25,32 @@
#ifndef __LOOT_GUI_LOOT_STATE__
#define __LOOT_GUI_LOOT_STATE__
#include "../backend/game/game.h"
#include "loot_settings.h"
#include "backend/game/game.h"
#include <include/cef_app.h>
#include <include/base/cef_lock.h>
#include <yaml-cpp/yaml.h>
namespace loot {
class LootState : public CefBase {
class LootState : public CefBase, public LootSettings {
public:
LootState();
void Init(const std::string& cmdLineGame);
const std::vector<std::string>& InitErrors() const;
void save(const boost::filesystem::path& file);
Game& CurrentGame();
void ChangeGame(const std::string& newGameFolder);
void UpdateGames(std::list<GameSettings>& games);
void UpdateGamesFromSettings();
// Get the folder names of the installed games.
std::vector<std::string> InstalledGames();
const YAML::Node& GetSettings() const;
void UpdateSettings(const YAML::Node& settings);
void SaveSettings();
// Used to check if LOOT has unaccepted sorting or metadata changes on quit.
int numUnappliedChanges;
private:
YAML::Node _settings;
std::list<Game> _games;
std::list<Game>::iterator _currentGame;
std::vector<std::string> _initErrors;
@@ -61,9 +58,8 @@ namespace loot {
// Select initial game.
void SelectGame(std::string cmdLineGame);
// Check if the settings file has the right root keys (doesn't check their values).
bool AreSettingsValid();
YAML::Node GetDefaultSettings() const;
static std::list<Game> ToGames(const std::vector<GameSettings>& settings);
static std::vector<GameSettings> ToGameSettings(const std::list<Game>& games);
// Lock used to protect access to member variables.
base::Lock _lock;
-44
View File
@@ -655,48 +655,4 @@ TEST_F(Game, shouldShowBlankEspAsActiveIfItHasBeenFullyLoadedAndTheGameHasBeenIn
EXPECT_FALSE(game.IsPluginActive("Blank.esp"));
}
TEST(ToGames, EmptySettings) {
EXPECT_EQ(std::list<loot::Game>(), loot::ToGames(std::list<loot::GameSettings>()));
}
TEST(ToGames, NonEmptySettings) {
std::list<loot::GameSettings> settings({
loot::GameSettings(loot::GameSettings::tes4),
loot::GameSettings(loot::GameSettings::tes5),
loot::GameSettings(loot::GameSettings::fo3),
loot::GameSettings(loot::GameSettings::fonv),
});
std::list<loot::Game> expected({
loot::Game(loot::Game::tes4),
loot::Game(loot::Game::tes5),
loot::Game(loot::Game::fo3),
loot::Game(loot::Game::fonv),
});
EXPECT_EQ(expected, loot::ToGames(settings));
}
TEST(ToGameSettings, EmptyGames) {
EXPECT_EQ(std::list<loot::GameSettings>(), loot::ToGameSettings(std::list<loot::Game>()));
}
TEST(ToGameSettings, NonEmptyGames) {
std::list<loot::Game> games({
loot::Game(loot::Game::tes4),
loot::Game(loot::Game::tes5),
loot::Game(loot::Game::fo3),
loot::Game(loot::Game::fonv),
});
std::list<loot::GameSettings> expected({
loot::GameSettings(loot::GameSettings::tes4),
loot::GameSettings(loot::GameSettings::tes5),
loot::GameSettings(loot::GameSettings::fo3),
loot::GameSettings(loot::GameSettings::fonv),
});
EXPECT_EQ(expected, loot::ToGameSettings(games));
}
#endif