Made config_cache 'just work' like old config loading code used to.
It still needs interface refactoring
This commit is contained in:
parent
c934825607
commit
e43513836a
4 changed files with 245 additions and 13 deletions
|
@ -12,11 +12,34 @@
|
|||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "filesystem.hpp"
|
||||
#include "config_cache.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "foreach.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
#include "log.hpp"
|
||||
#include "marked-up_text.hpp"
|
||||
#include "show_dialog.hpp"
|
||||
#include "sha1.hpp"
|
||||
#include "serialization/binary_wml.hpp"
|
||||
#include "serialization/parser.hpp"
|
||||
|
||||
#define ERR_CONFIG LOG_STREAM(err, config)
|
||||
#define LOG_CONFIG LOG_STREAM(info, config)
|
||||
#define DBG_CONFIG LOG_STREAM(debug, config)
|
||||
|
||||
#define ERR_FS LOG_STREAM(err, filesystem)
|
||||
|
||||
namespace game_config {
|
||||
config_cache config_cache::cache_;
|
||||
|
||||
config_cache& config_cache::instance()
|
||||
{
|
||||
return cache_;
|
||||
}
|
||||
|
||||
config_cache::config_cache() :
|
||||
game_config_(),
|
||||
force_valid_cache_(false),
|
||||
|
@ -37,6 +60,9 @@ namespace game_config {
|
|||
|
||||
void config_cache::set_config_root(const std::string& path)
|
||||
{
|
||||
if (path == config_root_)
|
||||
return;
|
||||
dirty_ = true;
|
||||
config_root_ = path;
|
||||
}
|
||||
|
||||
|
@ -45,6 +71,14 @@ namespace game_config {
|
|||
return user_config_root_;
|
||||
}
|
||||
|
||||
void config_cache::set_user_config_root(const std::string& path)
|
||||
{
|
||||
if (path == user_config_root_)
|
||||
return;
|
||||
dirty_ = true;
|
||||
user_config_root_ = path;
|
||||
}
|
||||
|
||||
const preproc_map& config_cache::get_preproc_map() const
|
||||
{
|
||||
return defines_map_;
|
||||
|
@ -76,27 +110,213 @@ namespace game_config {
|
|||
{
|
||||
if (dirty_)
|
||||
{
|
||||
reload_configs();
|
||||
load_configs(game_config_, false);
|
||||
} else {
|
||||
game_config_.reset_translation();
|
||||
game_config::load_config(game_config_.child("game_config"));
|
||||
}
|
||||
}
|
||||
|
||||
config& config_cache::get_config()
|
||||
config& config_cache::get_config(bool recheck_cache)
|
||||
{
|
||||
if (!dirty_)
|
||||
return game_config_;
|
||||
|
||||
reload_configs();
|
||||
dirty_ = true;
|
||||
load_configs(game_config_, recheck_cache);
|
||||
dirty_ = false;
|
||||
|
||||
return game_config_;
|
||||
}
|
||||
|
||||
void config_cache::reload_configs(bool recheck_cache)
|
||||
void config_cache::read_configs(config& cfg, std::string& error_log)
|
||||
{
|
||||
file_tree_checksum checksum = data_tree_checksum(recheck_cache);
|
||||
preproc_map defines_map(defines_map_);
|
||||
|
||||
std::string user_error_log;
|
||||
//read the file and then write to the cache
|
||||
scoped_istream stream = preprocess_file(config_root_, &defines_map, &error_log);
|
||||
|
||||
//reset the parse counter before reading the game files
|
||||
if (loadscreen::global_loadscreen) {
|
||||
loadscreen::global_loadscreen->parser_counter = 0;
|
||||
}
|
||||
|
||||
read(cfg, *stream, &error_log);
|
||||
// clone and put the gfx rules aside so that we can prepend the add-on
|
||||
// rules to them.
|
||||
config core_terrain_rules;
|
||||
// FIXME: there should be a canned algorithm for cloning child_list objects,
|
||||
// along with the memory their elements point to... little implementation detail.
|
||||
foreach(config const* p_cfg, game_config_.get_children("terrain_graphics")) {
|
||||
core_terrain_rules.add_child("terrain_graphics", *p_cfg);
|
||||
}
|
||||
game_config_.clear_children("terrain_graphics");
|
||||
|
||||
// load usermade add-ons
|
||||
// const std::string user_campaign_dir = get_addon_campaigns_dir();
|
||||
std::vector< std::string > error_addons;
|
||||
// Scan addon directories
|
||||
std::vector<std::string> user_addons;
|
||||
|
||||
get_files_in_dir(user_config_root_,NULL,&user_addons,ENTIRE_FILE_PATH);
|
||||
|
||||
// Load the addons
|
||||
for(std::vector<std::string>::const_iterator uc = user_addons.begin(); uc != user_addons.end(); ++uc) {
|
||||
std::string oldstyle_cfg = *uc + ".cfg";
|
||||
std::string main_cfg = *uc + "/_main.cfg";
|
||||
std::string toplevel;
|
||||
if (file_exists(oldstyle_cfg))
|
||||
toplevel = oldstyle_cfg;
|
||||
else if (file_exists(main_cfg))
|
||||
toplevel = main_cfg;
|
||||
else
|
||||
continue;
|
||||
|
||||
try {
|
||||
preproc_map addon_defines_map(defines_map);
|
||||
scoped_istream stream = preprocess_file(toplevel, &addon_defines_map);
|
||||
|
||||
std::string addon_error_log;
|
||||
|
||||
config umc_cfg;
|
||||
read(umc_cfg, *stream, &addon_error_log);
|
||||
|
||||
if (addon_error_log.empty()) {
|
||||
game_config_.append(umc_cfg);
|
||||
} else {
|
||||
user_error_log += addon_error_log;
|
||||
error_addons.push_back(*uc);
|
||||
}
|
||||
} catch(config::error& err) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
||||
error_addons.push_back(*uc);
|
||||
user_error_log += err.message + "\n";
|
||||
} catch(preproc_config::error&) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
||||
error_addons.push_back(*uc);
|
||||
//no need to modify the error log here, already done by the preprocessor
|
||||
} catch(io_exception&) {
|
||||
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
|
||||
error_addons.push_back(*uc);
|
||||
}
|
||||
if(error_addons.empty() == false) {
|
||||
std::stringstream msg;
|
||||
msg << _n("The following add-on had errors and could not be loaded:",
|
||||
"The following add-ons had errors and could not be loaded:",
|
||||
error_addons.size());
|
||||
for(std::vector<std::string>::const_iterator i = error_addons.begin(); i != error_addons.end(); ++i) {
|
||||
msg << "\n" << *i;
|
||||
}
|
||||
|
||||
msg << "\n" << _("ERROR DETAILS:") << "\n" << font::nullify_markup(user_error_log);
|
||||
|
||||
gui::show_error_message(*game_display::get_singleton(),msg.str());
|
||||
}
|
||||
}
|
||||
|
||||
cfg.merge_children("units");
|
||||
cfg.append(core_terrain_rules);
|
||||
|
||||
}
|
||||
|
||||
void config_cache::load_configs(config& cfg, bool recheck_cache)
|
||||
{
|
||||
bool is_valid = true;
|
||||
std::stringstream defines_string;
|
||||
for(preproc_map::const_iterator i = defines_map_.begin(); i != defines_map_.end(); ++i) {
|
||||
if(i->second.value != "" || i->second.arguments.empty() == false) {
|
||||
is_valid = false;
|
||||
ERR_CONFIG << "Preproc define not valid\n";
|
||||
break;
|
||||
}
|
||||
|
||||
defines_string << " " << i->first;
|
||||
}
|
||||
//std::string localename = get_locale().localename;
|
||||
//str << "-lang_" << (localename.empty() ? "default" : localename);
|
||||
|
||||
if(is_valid && use_cache_) {
|
||||
const std::string& cache = get_cache_dir();
|
||||
if(cache != "") {
|
||||
sha1_hash sha(defines_string.str()); // use a hash for a shorter display of the defines
|
||||
const std::string fname = cache + "/cache-v" + game_config::version + "-" + sha.display();
|
||||
const std::string fname_checksum = fname + ".checksum";
|
||||
|
||||
file_tree_checksum dir_checksum;
|
||||
|
||||
if(!force_valid_cache_) {
|
||||
try {
|
||||
if(file_exists(fname_checksum)) {
|
||||
config checksum_cfg;
|
||||
scoped_istream stream = istream_file(fname_checksum);
|
||||
read(checksum_cfg, *stream);
|
||||
dir_checksum = file_tree_checksum(checksum_cfg);
|
||||
}
|
||||
} catch(config::error&) {
|
||||
ERR_CONFIG << "cache checksum is corrupt\n";
|
||||
} catch(io_exception&) {
|
||||
ERR_CONFIG << "error reading cache checksum\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(force_valid_cache_) {
|
||||
LOG_CONFIG << "skipping cache validation (forced)\n";
|
||||
}
|
||||
|
||||
if(file_exists(fname) && (force_valid_cache_ || (dir_checksum == data_tree_checksum(recheck_cache)))) {
|
||||
LOG_CONFIG << "found valid cache at '" << fname << "' using it\n";
|
||||
log_scope("read cache");
|
||||
try {
|
||||
scoped_istream stream = istream_file(fname);
|
||||
read_compressed(cfg, *stream);
|
||||
return;
|
||||
} catch(config::error&) {
|
||||
ERR_CONFIG << "cache is corrupt. Loading from files\n";
|
||||
} catch(io_exception&) {
|
||||
ERR_CONFIG << "error reading cache. Loading from files\n";
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CONFIG << "no valid cache found. Writing cache to '" << fname << " with defines_map "<< defines_string.str() << "'\n";
|
||||
DBG_CONFIG << ((use_cache_ && file_exists(fname)) ? "yes":"no ") << " " << dir_checksum.modified << "==" << data_tree_checksum().modified << " " << dir_checksum.nfiles << "==" << data_tree_checksum().nfiles << " " << dir_checksum.sum_size << "==" << data_tree_checksum().sum_size << "\n";
|
||||
|
||||
std::string error_log;
|
||||
|
||||
read_configs(cfg, error_log);
|
||||
|
||||
if(!error_log.empty()) {
|
||||
gui::show_error_message(*game_display::get_singleton(),
|
||||
_("Warning: Errors occurred while loading game configuration files: '") +
|
||||
font::nullify_markup(error_log));
|
||||
|
||||
} else {
|
||||
try {
|
||||
scoped_ostream cache = ostream_file(fname);
|
||||
write_compressed(*cache, cfg);
|
||||
config checksum_cfg;
|
||||
data_tree_checksum().write(checksum_cfg);
|
||||
scoped_ostream checksum = ostream_file(fname_checksum);
|
||||
write(*checksum, checksum_cfg);
|
||||
} catch(io_exception&) {
|
||||
ERR_FS << "could not write to cache '" << fname << "'\n";
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_CONFIG << "caching cannot be done. Reading file\n";
|
||||
|
||||
std::string error_log;
|
||||
|
||||
read_configs(cfg, error_log);
|
||||
if(!error_log.empty()) {
|
||||
gui::show_error_message(*game_display::get_singleton(),
|
||||
_("Warning: Errors occurred while loading game configuration files: '") +
|
||||
font::nullify_markup(error_log));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void config_cache::set_use_cache(bool use)
|
||||
|
@ -106,6 +326,7 @@ namespace game_config {
|
|||
|
||||
void config_cache::add_define(const std::string& define)
|
||||
{
|
||||
dirty_ = true;
|
||||
defines_map_[define] = preproc_define();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ class config_cache : private boost::noncopyable {
|
|||
std::string config_root_, user_config_root_;
|
||||
preproc_map defines_map_;
|
||||
|
||||
void read_configs(config&, std::string&);
|
||||
|
||||
protected:
|
||||
config_cache();
|
||||
|
||||
|
@ -41,6 +43,7 @@ class config_cache : private boost::noncopyable {
|
|||
std::string get_config_root() const;
|
||||
std::string get_user_config_root() const;
|
||||
const preproc_map& get_preproc_map() const;
|
||||
void load_configs(config& cfg, bool recheck_cache);
|
||||
|
||||
public:
|
||||
static config_cache& instance();
|
||||
|
@ -48,13 +51,12 @@ class config_cache : private boost::noncopyable {
|
|||
void set_config_root(const std::string&);
|
||||
void set_user_config_root(const std::string&);
|
||||
|
||||
config& get_config();
|
||||
config& get_config(bool recheck_cache = false);
|
||||
|
||||
void clear_defines();
|
||||
void add_define(const std::string& define);
|
||||
|
||||
void reload_translations();
|
||||
void reload_configs(bool recheck_cache = false);
|
||||
|
||||
void set_use_cache(bool use);
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "config_cache.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_errors.hpp"
|
||||
|
@ -53,6 +54,9 @@ struct wesnoth_global_fixture {
|
|||
game_config::use_dummylocales = true;
|
||||
game_config::path = get_cwd();
|
||||
|
||||
load_language_list();
|
||||
::init_textdomains(game_config::config_cache::instance().get_config());
|
||||
|
||||
// Initialize unit tests
|
||||
SDL_Init(SDL_INIT_TIMER);
|
||||
test_utils::get_fake_display();
|
||||
|
@ -68,7 +72,6 @@ struct wesnoth_global_fixture {
|
|||
boost::unit_test::unit_test_monitor.register_exception_translator<game::error>(&exception_translator_game);
|
||||
boost::unit_test::unit_test_monitor.register_exception_translator<network::error>(&exception_translator_network);
|
||||
boost::unit_test::unit_test_monitor.register_exception_translator<config::error>(&exception_translator_config);
|
||||
load_language_list();
|
||||
}
|
||||
~wesnoth_global_fixture()
|
||||
{
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#define GETTEXT_DOMAIN "wesnoth"
|
||||
|
||||
#include <boost/test/auto_unit_test.hpp>
|
||||
|
||||
#include "config_cache.hpp"
|
||||
|
@ -102,6 +105,9 @@ BOOST_AUTO_TEST_CASE( test_load_config )
|
|||
std::string test_data_path("data/test/test/");
|
||||
cache.set_config_root(test_data_path);
|
||||
BOOST_CHECK_EQUAL(test_data_path, cache.get_config_root());
|
||||
test_data_path = "invalid";
|
||||
cache.set_user_config_root(test_data_path);
|
||||
BOOST_CHECK_EQUAL(test_data_path, cache.get_user_config_root());
|
||||
|
||||
config test_config;
|
||||
config* child = &test_config.add_child("textdomain");
|
||||
|
@ -116,7 +122,7 @@ BOOST_AUTO_TEST_CASE( test_load_config )
|
|||
cache.add_define("TEST_DEFINE");
|
||||
|
||||
child = &test_config.add_child("test_key");
|
||||
(*child)["define"] = _("testing translation reset");
|
||||
(*child)["define"] = _("testing translation reset.");
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(test_config, cache.get_config());
|
||||
|
@ -138,7 +144,7 @@ BOOST_AUTO_TEST_CASE( test_translation_reload )
|
|||
(*child)["define"] = "test";
|
||||
|
||||
child = &test_config.add_child("test_key");
|
||||
(*child)["define"] = _("testing translation reset");
|
||||
(*child)["define"] = _("testing translation reset.");
|
||||
|
||||
// Change language
|
||||
const std::vector<language_def>& languages = get_languages();
|
||||
|
@ -152,7 +158,7 @@ BOOST_AUTO_TEST_CASE( test_translation_reload )
|
|||
|
||||
BOOST_CHECK_MESSAGE( test_config != cache.get_config(), "Translation update failed update translations!" );
|
||||
|
||||
(*child)["define"] = _("test translation reset");
|
||||
(*child)["define"] = _("testing translation reset.");
|
||||
|
||||
BOOST_CHECK_EQUAL(test_config, cache.get_config());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue