The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
game_config_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013 - 2016 by Andrius Silinskas <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 #include "game_config_manager.hpp"
15 
16 #include "about.hpp"
17 #include "addon/manager.hpp"
18 #include "ai/configuration.hpp"
19 #include "cursor.hpp"
20 #include "game_config.hpp"
21 #include "gettext.hpp"
22 #include "game_classification.hpp"
25 #include "hotkey/hotkey_item.hpp"
27 #include "language.hpp"
28 #include "log.hpp"
29 #include "preferences.hpp"
31 #include "terrain/builder.hpp"
32 #include "terrain/type_data.hpp"
33 #include "units/types.hpp"
34 #include "version.hpp"
35 #include "theme.hpp"
36 #include "image.hpp"
37 
38 #include <boost/make_shared.hpp>
39 
40 static lg::log_domain log_config("config");
41 #define ERR_CONFIG LOG_STREAM(err, log_config)
42 #define WRN_CONFIG LOG_STREAM(warn, log_config)
43 #define LOG_CONFIG LOG_STREAM(info, log_config)
44 
46 
48  const commandline_options& cmdline_opts,
49  CVideo& video,
50  const bool jump_to_editor) :
51  cmdline_opts_(cmdline_opts),
52  video_(video),
53  jump_to_editor_(jump_to_editor),
54  game_config_(),
55  old_defines_map_(),
56  paths_manager_(),
57  cache_(game_config::config_cache::instance())
58 {
59  assert(!singleton);
60  singleton = this;
61 
63  cache_.set_use_cache(false);
64  }
67  }
68 }
69 
71 {
72  assert(singleton);
73  singleton = nullptr;
74 }
75 
77  return singleton;
78 }
79 
81 {
82  // Add preproc defines according to the command line arguments.
83  game_config::scoped_preproc_define multiplayer("MULTIPLAYER",
88  game_config::scoped_preproc_define title_screen("TITLE_SCREEN",
90 
92 
94 
97 
102 
103  return true;
104 }
105 
106 namespace {
107 /// returns true if every define in special is also defined in general
108 bool map_includes(const preproc_map& general, const preproc_map& special)
109 {
110  for (const preproc_map::value_type& pair : special)
111  {
112  preproc_map::const_iterator it = general.find(pair.first);
113  if (it == general.end() || it->second != pair.second) {
114  return false;
115  }
116  }
117  return true;
118 }
119 } // end anonymous namespace
120 
123 {
124  game_config::scoped_preproc_define debug_mode("DEBUG_MODE",
126 
127  // Game_config already holds requested config in memory.
128  if (!game_config_.empty()) {
129  if ((force_reload == NO_FORCE_RELOAD) && old_defines_map_ == cache_.get_preproc_map()) {
130  return;
131  }
132  if ((force_reload == NO_INCLUDE_RELOAD) && map_includes(old_defines_map_, cache_.get_preproc_map())) {
133  return;
134  }
135  }
136 
137  gui2::tloadscreen::display(video_, [this, force_reload, classification]() {
138  load_game_config(force_reload, classification);
139  });
140 }
141 
144 {
145  // Make sure that 'debug mode' symbol is set
146  // if command line parameter is selected
147  // also if we're in multiplayer and actual debug mode is disabled.
148 
149  // The loadscreen will erase the titlescreen.
150  // NOTE: even without loadscreen, needed after MP lobby.
151  try {
152  // Read all game configs.
153  // First we load all core configs, the mainline one and the ones from the addons.
154  // Validate the cores and discard the invalid.
155  // Then find the path to the selected core.
156  // Load the selected core.
157  // Handle terrains so that they are last loaded from the core.
158  // Load every compatible addon.
159  gui2::tloadscreen::progress("verify cache");
161  gui2::tloadscreen::progress("create cache");
162 
163  // Start transaction so macros are shared.
164  game_config::config_cache_transaction main_transaction;
165 
166  config cores_cfg;
167  // Load mainline cores definition file.
168  cache_.get_config(game_config::path + "/data/cores.cfg", cores_cfg);
169 
170  // Append the $user_campaign_dir/*/cores.cfg files to the cores.
171  std::vector<std::string> user_dirs;
172  {
173  const std::string user_campaign_dir = filesystem::get_addons_dir();
174  std::vector<std::string> user_files;
175  filesystem::get_files_in_dir(user_campaign_dir, &user_files, &user_dirs,
177  }
178  for (const std::string& umc : user_dirs) {
179  const std::string cores_file = umc + "/cores.cfg";
180  if (filesystem::file_exists(cores_file)) {
181  config cores;
182  cache_.get_config(cores_file, cores);
183  cores_cfg.append(cores);
184  }
185  }
186 
187  // Validate every core
188  config valid_cores;
189  bool current_core_valid = false;
190  std::string wml_tree_root;
191  for (const config& core : cores_cfg.child_range("core")) {
192 
193  const std::string& id = core["id"];
194  if (id.empty()) {
196  _("Error validating data core."),
197  _("Found a core without id attribute.")
198  + '\n' + _("Skipping the core."),
199  video_);
200  continue;
201  }
202  if (*&valid_cores.find_child("core", "id", id)) {
204  _("Error validating data core."),
205  _("Core ID: ") + id
206  + '\n' + _("The ID is already in use.")
207  + '\n' + _("Skipping the core."),
208  video_);
209  continue;
210  }
211 
212  const std::string& path = core["path"];
215  _("Error validating data core."),
216  _("Core ID: ") + id
217  + '\n' + _("Core Path: ") + path
218  + '\n' + _("File not found.")
219  + '\n' + _("Skipping the core."),
220  video_);
221  continue;
222  }
223 
224  if (id == "default" && !current_core_valid) {
225  wml_tree_root = path;
226  }
227  if (id == preferences::core_id()) {
228  current_core_valid = true;
229  wml_tree_root = path;
230  }
231 
232  valid_cores.add_child("core", core); // append(core);
233  }
234 
235  if (!current_core_valid) {
237  _("Error loading core data."),
238  _("Core ID: ") + preferences::core_id()
239  + '\n' + _("Error loading the core with named id.")
240  + '\n' + _("Falling back to the default core."),
241  video_);
242  preferences::set_core_id("default");
243  }
244 
245  // check if we have a valid default core which should always be the case.
246  if (wml_tree_root.empty()) {
248  _("Error loading core data."),
249  _("Can't locate the default core.")
250  + '\n' + _("The game will now exit."),
251  video_);
252  throw;
253  }
254 
255  // Load the selected core
257  game_config_.append(valid_cores);
258 
259  main_transaction.lock();
260 
261  // Put the gfx rules aside so that we can prepend the add-on
262  // rules to them.
263  config core_terrain_rules;
264  core_terrain_rules.splice_children(game_config_, "terrain_graphics");
265 
267  load_addons_cfg();
268 
269  // If multiplayer campaign is being loaded, [scenario] tags should
270  // become [multiplayer] tags and campaign's id should be added to them
271  // to allow to recognize which scenarios belongs to a loaded campaign.
272  if (classification != nullptr) {
273  if (const config& campaign = game_config().find_child("campaign", "id", classification->campaign))
274  {
275  const bool require_campaign = campaign["require_campaign"].to_bool(true);
276  for (config& scenario : game_config_.child_range("scenario"))
277  {
278  scenario["require_scenario"] = require_campaign;
279  for (config& side : scenario.child_range("side"))
280  {
281  side["no_leader"] = side["no_leader"].to_bool(true);
282  }
283  }
284  }
285  }
286 
287  // Extract the Lua scripts at toplevel.
290 
291  // Put the gfx rules back to game config.
292  game_config_.splice_children(core_terrain_rules, "terrain_graphics");
293 
295  set_color_info();
296  set_unit_data();
297 
299  tdata_ = boost::make_shared<terrain_type_data>(game_config_);
302  } catch(game::error& e) {
303  ERR_CONFIG << "Error loading game configuration files\n" << e.message << '\n';
304 
305  // Try reloading without add-ons
306  if (!game_config::no_addons) {
307  game_config::no_addons = true;
309  _("Error loading custom game configuration files. The game will try without loading add-ons."),
310  e.message, video_);
311  load_game_config(force_reload, classification);
312  } else if (preferences::core_id() != "default") {
314  _("Error loading custom game configuration files. The game will fallback to the default core files."),
315  e.message, video_);
316  preferences::set_core_id("default");
317  game_config::no_addons = false;
318  load_game_config(force_reload, classification);
319  } else {
321  _("Error loading default core game configuration files. The game will now exit."),
322  e.message, video_);
323  throw;
324  }
325  }
326 
328 
329  // Set new binary paths.
331 }
332 
333 struct addon_source {
337 };
338 
340 {
341  const std::string user_campaign_dir = filesystem::get_addons_dir();
342 
343  std::vector<std::string> error_addons;
344  std::vector<std::string> user_dirs;
345  std::vector<std::string> user_files;
346  std::vector<addon_source> addons_to_load;
347 
348  filesystem::get_files_in_dir(user_campaign_dir, &user_files, &user_dirs,
350 
351  std::vector<std::string> error_log;
352 
353  // Append the $user_campaign_dir/*.cfg files to addons_to_load.
354  for(const std::string& uc : user_files) {
355  const std::string file = uc;
356  const int size_minus_extension = file.size() - 4;
357  if(file.substr(size_minus_extension, file.size()) == ".cfg") {
358  const int userdata_loc = file.find("data/add-ons") + 5;
359  ERR_CONFIG << "error reading usermade add-on '"
360  << file << "'\n";
361  error_addons.push_back(file);
362  error_log.push_back("The format '~" + file.substr(userdata_loc)
363  + "' (for single-file add-ons) is not supported anymore, use '~"
364  + file.substr(userdata_loc,
365  size_minus_extension - userdata_loc)
366  + "/_main.cfg' instead.");
367  }
368  }
369 
370  // Rerun the directory scan using filename only, to get the addon_ids more easily.
371  user_files.clear();
372  user_dirs.clear();
373  filesystem::get_files_in_dir(user_campaign_dir, &user_files, &user_dirs,
375 
376  // Append the $user_campaign_dir/*/_main.cfg files to addons_to_load.
377  for (const std::string& uc : user_dirs) {
378  const std::string addon_id = uc;
379  const std::string addon_dir = user_campaign_dir + "/" + uc;
380 
381  const std::string main_cfg = addon_dir + "/_main.cfg";
382  const std::string info_cfg = addon_dir + "/_info.cfg";
383 
384  addon_source addon;
385  addon.main_cfg = main_cfg;
386  addon.addon_id = addon_id;
387 
388  if (filesystem::file_exists(main_cfg)) {
389  if (filesystem::file_exists(info_cfg)) {
390  config info;
391  cache_.get_config(info_cfg, info);
392  const config info_tag = info.child_or_empty("info");
393  std::string core = info_tag["core"];
394  if (core.empty()) core = "default";
395  if ( !info_tag.empty() && // Don't skip addons which have no [info], they are most likely manually installed.
396  info_tag["type"] != "core" && // Don't skip cores, we want them selectable at all times.
397  core != preferences::core_id() // Don't skip addons matching our current core.
398  ) {
399  continue; // Skip add-ons not matching our current core.
400  }
401  }
402 
403  // Ask the addon manager to find version info for us (from info, pbl file)
404  addon.version = get_addon_version_info(addon_id);
405  addons_to_load.push_back(addon);
406  }
407  }
408 
409  // Load the addons.
410  for (const addon_source & addon : addons_to_load) {
411  try {
412  // Load this addon from the cache, to a config
413  config umc_cfg;
414  cache_.get_config(addon.main_cfg, umc_cfg);
415 
416  // Annotate "era", "modification", and scenario tags with addon_id info
417  const char * tags_with_addon_id [] = { "era", "modification", "multiplayer", "scenario", nullptr };
418 
419  for (const char ** type = tags_with_addon_id; *type; type++)
420  {
421  for (config & cfg : umc_cfg.child_range(*type)) {
422  cfg["addon_id"] = addon.addon_id;
423  // Note that this may reformat the string in a canonical form.
424  cfg["addon_version"] = addon.version.str();
425  }
426  }
427 
428  game_config_.append(umc_cfg);
429  } catch(config::error& err) {
430  ERR_CONFIG << "error reading usermade add-on '" << addon.main_cfg << "'" << std::endl;
431  ERR_CONFIG << err.message << '\n';
432  error_addons.push_back(addon.main_cfg);
433  error_log.push_back(err.message);
434  } catch(preproc_config::error& err) {
435  ERR_CONFIG << "error reading usermade add-on '" << addon.main_cfg << "'" << std::endl;
436  ERR_CONFIG << err.message << '\n';
437  error_addons.push_back(addon.main_cfg);
438  error_log.push_back(err.message);
439  } catch(filesystem::io_exception&) {
440  ERR_CONFIG << "error reading usermade add-on '" << addon.main_cfg << "'" << std::endl;
441  error_addons.push_back(addon.main_cfg);
442  }
443  }
444  if(error_addons.empty() == false) {
445  const size_t n = error_addons.size();
446  const std::string& msg1 =
447  _n("The following add-on had errors and could not be loaded:",
448  "The following add-ons had errors and could not be loaded:",
449  n);
450  const std::string& msg2 =
451  _n("Please report this to the author or maintainer of this add-on.",
452  "Please report this to the respective authors or maintainers of these add-ons.",
453  n);
454 
455  const std::string& report = utils::join(error_log, "\n\n");
456 
457  gui2::twml_error::display(msg1, msg2, error_addons, report,
458  video_);
459  }
460 }
461 
463 {
464  config& hashes = game_config_.add_child("multiplayer_hashes");
465  for (const config &ch : game_config_.child_range("multiplayer")) {
466  hashes[ch["id"]] = ch.hash();
467  }
468 }
469 
471 {
472  config colorsys_info;
473  colorsys_info.splice_children(game_config_, "color_range");
474  colorsys_info.splice_children(game_config_, "color_palette");
475  game_config::add_color_info(colorsys_info);
476 }
477 
479 {
480  game_config_.merge_children("units");
481  gui2::tloadscreen::progress("load unit types");
482  if(config &units = game_config_.child("units")) {
484  }
485 }
486 
488 {
489  // Rebuild addon version info cache.
491 
492  // Force a reload of configuration information.
494  old_defines_map_.clear();
497 }
498 
500 {
503 }
504 
507 {
508  game_config::scoped_preproc_define difficulty(classification.difficulty,
509  !classification.difficulty.empty());
510  game_config::scoped_preproc_define campaign(classification.campaign_define,
511  !classification.campaign_define.empty());
512  game_config::scoped_preproc_define scenario(classification.scenario_define,
513  !classification.scenario_define.empty());
515  !classification.era_define.empty());
516  game_config::scoped_preproc_define multiplayer("MULTIPLAYER",
517  classification.campaign_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER);
519  classification.campaign_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER);
520 
522  std::deque<define> extra_defines;
523  for (const std::string& extra_define : classification.campaign_xtra_defines) {
524  define new_define(new game_config::scoped_preproc_define(extra_define));
525  extra_defines.push_back(new_define);
526  }
527  std::deque<define> modification_defines;
528  for (const std::string& mod_define : classification.mod_defines) {
529  define new_define(new game_config::scoped_preproc_define(mod_define, !mod_define.empty()));
530  modification_defines.push_back(new_define);
531  }
532 
533  try{
535  }
536  catch(game::error&) {
538 
539  std::deque<define> previous_defines;
540  for (const preproc_map::value_type& preproc : old_defines_map_) {
541  define new_define(new game_config::scoped_preproc_define(preproc.first));
542  previous_defines.push_back(new_define);
543  }
544 
546 
547  throw;
548  }
549 }
551 {
552  game_config::scoped_preproc_define multiplayer("MULTIPLAYER", is_mp);
553  game_config::scoped_preproc_define mptest("MP_TEST", cmdline_opts_.mptest && is_mp);
554 ///During an mp game the default difficuly define is also defined so better already load it now if we alreeady must reload config cache.
556 
558  try{
560  }
561  catch(game::error&) {
563 
564  std::deque<define> previous_defines;
565  for (const preproc_map::value_type& preproc : old_defines_map_) {
566  define new_define(new game_config::scoped_preproc_define(preproc.first));
567  previous_defines.push_back(new_define);
568  }
569 
571 
572  throw;
573  }
574 }
575 
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void set_config(config &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1005
Dont reload if the previous defines include the new defines.
bool mptest
True if –mp-test was given on the command line.
void set_paths(const config &cfg)
static void display(const std::string &summary, const std::string &post_summary, const std::vector< std::string > &files, const std::string &details, CVideo &video)
The display function; see tdialog for more information.
Definition: wml_error.hpp:42
std::string era()
game_classification * classification
Definition: resources.cpp:37
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:566
static void set_known_themes(const config *cfg)
Definition: theme.cpp:912
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
void merge_children(const std::string &key)
All children with the given key will be merged into the first element with that key.
Definition: config.cpp:574
bool noaddons
True if –noaddons was given on the command line. Disables the loading of all add-ons.
logger & info()
Definition: log.cpp:91
Definition: video.hpp:58
void lock()
Lock the transaction so no more macros are added.
void set_scope_active(scope s, bool set)
static int report(lua_State *L, int status)
Definition: lua.cpp:134
filesystem::binary_paths_manager paths_manager_
const std::string DEFAULT_DIFFICULTY
The default difficulty setting for campaigns.
#define ERR_CONFIG
game_config_manager(const commandline_options &cmdline_opts, CVideo &video, const bool jump_to_editor)
unit_type_data unit_types
Definition: types.cpp:1314
Dont reload if the previous defines equal the new defines.
static lg::log_domain log_config("config")
const config & child_or_empty(const std::string &key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:722
void clear_children(const std::string &key)
Definition: config.cpp:820
bool empty() const
Definition: config.cpp:1105
void load_config(const config &v)
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on's version information.
Definition: manager.cpp:379
std::map< std::string, preproc_define > preproc_map
Used to set and unset scoped defines to preproc_map.
void clear_binary_paths_cache()
static void extract_preload_scripts(config const &game_config)
static game_config_manager * get()
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
GLsizei const char ** path
Definition: glew.h:4654
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
Definition: config.cpp:837
static text_list cache_
Definition: font.cpp:772
void add_color_info(const config &v)
std::string campaign_define
If there is a define the campaign uses to customize data.
const preproc_map & get_preproc_map() const
void get_config(const std::string &path, config &cfg)
Gets a config object from given path.
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:86
config & add_child(const std::string &key)
Definition: config.cpp:743
void set_force_valid_cache(bool force)
Enable/disable cache validation.
void set_core_id(const std::string &core_id)
static game_config_manager * singleton
std::string path
boost::optional< std::string > test
Non-empty if –test was given on the command line. Goes directly into test mode, into a scenario...
void init_textdomains(const config &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:293
bool multiplayer
True if –multiplayer was given on the command line. Goes directly into multiplayer mode...
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
Definition: manager.cpp:337
std::string campaign
the campaign being played
static void display(CVideo &video, std::function< void()> f)
Definition: loadscreen.cpp:181
void deactivate_all_scopes()
log_domain & general()
Definition: log.cpp:103
std::string era_define
If there is a define the era uses to customize data.
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
void load_game_config_for_create(bool is_mp)
void set_use_cache(bool use)
Enable/disable caching.
static void progress(const char *stage_name=nullptr)
Definition: loadscreen.cpp:128
void clear_defines()
Clear stored defines map to default values.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
bool nocache
True if –nocache was given on the command line. Disables cache usage.
logger & err()
Definition: log.cpp:79
std::vector< std::string > campaign_xtra_defines
more customization of data
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
Game configuration data as global variables.
Definition: build_info.cpp:38
An exception object used when an IO error occurs.
Definition: filesystem.hpp:40
const config & game_config() const
std::string scenario_define
If there is a define the scenario uses to customize data.
std::vector< std::string > mod_defines
If there are defines the modifications use to customize data.
Definitions related to theme-support.
Definitions for the terrain builder.
Represents version numbers.
Definition: version.hpp:44
const file_tree_checksum & data_tree_checksum(bool reset=false)
Get the time at which the data/ tree was last modified at.
std::string difficulty
The difficulty level the game is being played on.
std::string core_id()
void set_about(const config &cfg)
Definition: about.cpp:132
Used to share macros between cache objects You have to create transaction object to load all macros t...
std::string hash() const
Definition: config.cpp:1468
void load_game_config_for_game(const game_classification &classification)
GLclampd n
Definition: glew.h:5903
static void set_terrain_rules_cfg(const config &cfg)
Set the config where we will parse the global terrain rules.
Definition: builder.cpp:284
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:27
void load_hotkeys(const config &cfg, bool set_as_default)
Iterates through all hotkeys present in the config struct and creates and adds them to the hotkey lis...
std::string get_addons_dir()
Managing the AIs configuration - headers.
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:1010
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:29
version_info version
const commandline_options & cmdline_opts_
bool validcache
True if –validcache was given on the command line. Makes Wesnoth assume the cache is valid...
bool init_strings(const config &cfg)
Initializes certain English strings.
Definition: language.cpp:316
game_config::config_cache & cache_
#define e
static void init(const config &game_config)
Init the parameters of ai configuration parser.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool init_game_config(FORCE_RELOAD_CONFIG force_reload)
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
void load_game_config(FORCE_RELOAD_CONFIG force_reload, game_classification const *classification=nullptr)
static void test()
void recheck_filetree_checksum()
Force cache checksum validation.
void load_game_config_with_loadscreen(FORCE_RELOAD_CONFIG force_reload, game_classification const *classification=nullptr)