The Battle for Wesnoth  1.19.7+dev
preferences.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2024 - 2024
3  Part of the Battle for Wesnoth Project https://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 
15 /**
16  * @file
17  * Get and set user-preferences.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
23 
24 #include "cursor.hpp"
25 #include "game_board.hpp"
26 #include "game_display.hpp"
27 #include "formula/string_utils.hpp"
28 #include "game_config.hpp"
29 #include "game_data.hpp"
30 #include "gettext.hpp"
34 #include "hotkey/hotkey_item.hpp"
35 #include "log.hpp"
36 #include "map_settings.hpp"
37 #include "map/map.hpp"
38 #include "resources.hpp"
39 #include "serialization/chrono.hpp"
40 #include "serialization/parser.hpp"
41 #include "sound.hpp"
42 #include "units/unit.hpp"
43 #include "video.hpp"
44 
45 #include <sys/stat.h> // for setting the permissions of the preferences file
46 #include <boost/algorithm/string.hpp>
47 
48 #ifdef _WIN32
50 #include <windows.h>
51 #endif
52 
53 #ifndef __APPLE__
54 #include <openssl/evp.h>
55 #include <openssl/err.h>
56 #else
57 #include <CommonCrypto/CommonCryptor.h>
58 #endif
59 
60 static lg::log_domain log_config("config");
61 #define ERR_CFG LOG_STREAM(err , log_config)
62 #define DBG_CFG LOG_STREAM(debug , log_config)
63 
64 static lg::log_domain log_filesystem("filesystem");
65 #define ERR_FS LOG_STREAM(err, log_filesystem)
66 
67 static lg::log_domain advanced_preferences("advanced_preferences");
68 #define ERR_ADV LOG_STREAM(err, advanced_preferences)
69 
70 using namespace std::chrono_literals;
71 
73 : preferences_()
74 , fps_(false)
75 , completed_campaigns_()
76 , encountered_units_set_()
77 , encountered_terrains_set_()
78 , history_map_()
79 , acquaintances_()
80 , option_values_()
81 , options_initialized_(false)
82 , mp_modifications_()
83 , mp_modifications_initialized_(false)
84 , sp_modifications_()
85 , sp_modifications_initialized_(false)
86 , message_private_on_(false)
87 , credentials_()
88 , advanced_prefs_()
89 {
92 
93  // make sure this has a default set
94  if(!preferences_.has_attribute("scroll_threshold")) {
95  preferences_[prefs_list::scroll_threshold] = 10;
96  }
97 
98  for(const config& acfg : preferences_.child_range(prefs_list::acquaintance)) {
100  acquaintances_[ac.get_nick()] = ac;
101  }
102 }
103 
105 {
106  config campaigns;
107  for(const auto& elem : completed_campaigns_) {
108  config cmp;
109  cmp["name"] = elem.first;
110  cmp["difficulty_levels"] = utils::join(elem.second);
111  campaigns.add_child("campaign", cmp);
112  }
113 
114  set_child(prefs_list::completed_campaigns, campaigns);
115 
116  preferences_[prefs_list::encountered_units] = utils::join(encountered_units_set_);
118  preferences_[prefs_list::encountered_terrain_list] = t_translation::write_list(terrain);
119 
120  /* Structure of the history
121  [history]
122  [history_id]
123  [line]
124  message = foobar
125  [/line]
126  */
127  config history;
128  for(const auto& history_id : history_map_) {
129  config history_id_cfg; // [history_id]
130  for(const std::string& line : history_id.second) {
131  config cfg; // [line]
132 
133  cfg["message"] = line;
134  history_id_cfg.add_child("line", std::move(cfg));
135  }
136 
137  history.add_child(history_id.first, history_id_cfg);
138  }
139  set_child(prefs_list::history, history);
140 
141  preferences_.clear_children(prefs_list::acquaintance);
142 
143  for(auto& a : acquaintances_) {
144  config& item = preferences_.add_child(prefs_list::acquaintance);
145  a.second.save(item);
146  }
147 
148  history_map_.clear();
149  encountered_units_set_.clear();
151 
152  try {
153  if(!no_preferences_save) {
155  }
156  } catch (...) {
157  ERR_FS << "Failed to write preferences due to exception: " << utils::get_unknown_exception_type();
158  }
159 }
160 
162 {
163  for(const config& pref : gc.child_range("advanced_preference")) {
164  try {
165  advanced_prefs_.emplace_back(pref);
166  } catch(const std::invalid_argument& e) {
167  ERR_ADV << e.what();
168  continue;
169  }
170  }
171 
172  // show_deprecation has a different default on the dev branch
173  if(game_config::wesnoth_version.is_dev_version()) {
175  if(op.field == prefs_list::show_deprecation) {
176  op.cfg["default"] = true;
177  }
178  }
179  }
180 
181  std::sort(advanced_prefs_.begin(), advanced_prefs_.end(), [](const auto& lhs, const auto& rhs) { return translation::icompare(lhs.name, rhs.name) < 0; });
182 }
183 
184 void prefs::migrate_preferences(const std::string& migrate_prefs_file)
185 {
186  if(migrate_prefs_file != filesystem::get_synced_prefs_file() && filesystem::file_exists(migrate_prefs_file)) {
187  // if the file doesn't exist, just copy the file over
188  // else need to merge the preferences file
191  } else {
192  config current_cfg;
194  read(current_cfg, *current_stream);
195  config old_cfg;
196  filesystem::scoped_istream old_stream = filesystem::istream_file(migrate_prefs_file, false);
197  read(old_cfg, *old_stream);
198 
199  // when both files have the same attribute, use the one from whichever was most recently modified
200  bool current_prefs_are_older = filesystem::file_modified_time(filesystem::get_synced_prefs_file()) < filesystem::file_modified_time(migrate_prefs_file);
201  for(const auto& [key, value] : old_cfg.attribute_range()) {
202  if(current_prefs_are_older || !current_cfg.has_attribute(key)) {
203  preferences_[key] = value;
204  }
205  }
206 
207  // don't touch child tags
208 
210  }
211  }
212 }
214 {
218 }
219 
220 std::set<std::string> prefs::all_attributes()
221 {
222  std::set<std::string> attrs;
223 
224  // attributes that exist in the preferences file
225  for(const auto& attr : preferences_.attribute_range()) {
226  attrs.emplace(attr.first);
227  }
228  // all mainline preference attributes, whether they're set or not
229  for(const auto attr : prefs_list::values) {
230  attrs.emplace(attr);
231  }
232 
233  return attrs;
234 }
235 
237 {
239  try{
240  config default_prefs;
241  config unsynced_prefs;
242  config synced_prefs;
243 #ifdef DEFAULT_PREFS_PATH
244  // NOTE: the system preferences file is only ever relevant for the first time wesnoth starts
245  // any default values will subsequently be written to the normal preferences files, which takes precedence over any values in the system preferences file
246  {
248  read(default_prefs, *stream);
249  }
250 #endif
251  {
253  read(unsynced_prefs, *stream);
254  }
255 
256  {
258  read(synced_prefs, *stream);
259  }
260 
261  preferences_.merge_with(default_prefs);
262  preferences_.merge_with(unsynced_prefs);
263  preferences_.merge_with(synced_prefs);
264 
265  // check for any unknown preferences
266  for(const auto& [key, _] : synced_prefs.attribute_range()) {
267  if(std::find(synced_attributes_.begin(), synced_attributes_.end(), key) == synced_attributes_.end()) {
268  unknown_synced_attributes_.insert(key);
269  }
270  }
271  for(const auto& [key, _] : unsynced_prefs.attribute_range()) {
272  if(std::find(unsynced_attributes_.begin(), unsynced_attributes_.end(), key) == unsynced_attributes_.end()) {
273  unknown_unsynced_attributes_.insert(key);
274  }
275  }
276 
277  for(const auto [key, _] : synced_prefs.all_children_view()) {
278  if(std::find(synced_children_.begin(), synced_children_.end(), key) == synced_children_.end()) {
279  unknown_synced_children_.insert(key);
280  }
281  }
282  for(const auto [key, _] : unsynced_prefs.all_children_view()) {
283  if(std::find(unsynced_children_.begin(), unsynced_children_.end(), key) == unsynced_children_.end()) {
284  unknown_unsynced_children_.insert(key);
285  }
286  }
287  } catch(const config::error& e) {
288  ERR_CFG << "Error loading preference, message: " << e.what();
289  }
290 
293 
294  /*
295  completed_campaigns = "A,B,C"
296  [completed_campaigns]
297  [campaign]
298  name = "A"
299  difficulty_levels = "EASY,MEDIUM"
300  [/campaign]
301  [/completed_campaigns]
302  */
303  // presumably for backwards compatibility?
304  // nothing actually sets the attribute, only the child tags
305  for(const std::string& c : utils::split(preferences_[prefs_list::completed_campaigns])) {
306  completed_campaigns_[c]; // create the elements
307  }
308 
309  if(auto ccc = get_child(prefs_list::completed_campaigns)) {
310  for(const config& cc : ccc->child_range("campaign")) {
311  std::set<std::string>& d = completed_campaigns_[cc["name"]];
312  std::vector<std::string> nd = utils::split(cc["difficulty_levels"]);
313  std::copy(nd.begin(), nd.end(), std::inserter(d, d.begin()));
314  }
315  }
316 
317  encountered_units_set_ = utils::split_set(preferences_[prefs_list::encountered_units].str());
318 
319  const t_translation::ter_list terrain(t_translation::read_list(preferences_[prefs_list::encountered_terrain_list].str()));
320  encountered_terrains_set_.insert(terrain.begin(), terrain.end());
321 
322  if(auto history = get_child(prefs_list::history)) {
323  /* Structure of the history
324  [history]
325  [history_id]
326  [line]
327  message = foobar
328  [/line]
329  */
330  for(const auto [key, cfg] : history->all_children_view()) {
331  for(const config& l : cfg.child_range("line")) {
332  history_map_[key].push_back(l["message"]);
333  }
334  }
335  }
336 }
337 
339 {
340 #ifndef _WIN32
341  bool synced_prefs_file_existed = filesystem::file_exists(filesystem::get_synced_prefs_file());
342  bool unsynced_prefs_file_existed = filesystem::file_exists(filesystem::get_unsynced_prefs_file());
343 #endif
344 
345  config synced;
346  config unsynced;
347 
348  for(const char* attr : synced_attributes_) {
349  if(preferences_.has_attribute(attr)) {
350  synced[attr] = preferences_[attr];
351  }
352  }
353  for(const char* attr : synced_children_) {
354  for(const auto& child : preferences_.child_range(attr)) {
355  synced.add_child(attr, child);
356  }
357  }
358 
359  for(const char* attr : unsynced_attributes_) {
360  if(preferences_.has_attribute(attr)) {
361  unsynced[attr] = preferences_[attr];
362  }
363  }
364  for(const char* attr : unsynced_children_) {
365  for(const auto& child : preferences_.child_range(attr)) {
366  unsynced.add_child(attr, child);
367  }
368  }
369 
370  // write any unknown preferences back out
371  for(const std::string& attr : unknown_synced_attributes_) {
372  synced[attr] = preferences_[attr];
373  }
374  for(const std::string& attr : unknown_synced_children_) {
375  for(const auto& child : preferences_.child_range(attr)) {
376  synced.add_child(attr, child);
377  }
378  }
379 
380  for(const std::string& attr : unknown_unsynced_attributes_) {
381  unsynced[attr] = preferences_[attr];
382  }
383  for(const std::string& attr : unknown_unsynced_children_) {
384  for(const auto& child : preferences_.child_range(attr)) {
385  unsynced.add_child(attr, child);
386  }
387  }
388 
389  try {
391  write(*synced_prefs_file, synced);
392  } catch(const filesystem::io_exception&) {
393  ERR_FS << "error writing to synced preferences file '" << filesystem::get_synced_prefs_file() << "'";
394  }
395 
396  try {
398  write(*unsynced_prefs_file, unsynced);
399  } catch(const filesystem::io_exception&) {
400  ERR_FS << "error writing to unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
401  }
402 
404 
405 #ifndef _WIN32
406  if(!synced_prefs_file_existed) {
407  if(chmod(filesystem::get_synced_prefs_file().c_str(), 0600) == -1) {
408  ERR_FS << "error setting permissions of preferences file '" << filesystem::get_synced_prefs_file() << "'";
409  }
410  }
411  if(!unsynced_prefs_file_existed) {
412  if(chmod(filesystem::get_unsynced_prefs_file().c_str(), 0600) == -1) {
413  ERR_FS << "error setting permissions of unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
414  }
415  }
416 #endif
417 }
418 
420 {
421  // Zero them before clearing.
422  // Probably overly paranoid, but doesn't hurt?
423  for(auto& cred : credentials_) {
424  std::fill(cred.username.begin(), cred.username.end(), '\0');
425  std::fill(cred.server.begin(), cred.server.end(), '\0');
426  }
427  credentials_.clear();
428 }
429 
431 {
432  if(!remember_password()) {
433  return;
434  }
436  std::string cred_file = filesystem::get_credentials_file();
437  if(!filesystem::file_exists(cred_file)) {
438  return;
439  }
440  filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
441  // Credentials file is a binary blob, so use streambuf iterator
442  preferences::secure_buffer data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
444  if(data.empty() || data[0] != pref_constants::CREDENTIAL_SEPARATOR) {
445  ERR_CFG << "Invalid data in credentials file";
446  return;
447  }
448  for(const std::string& elem : utils::split(std::string(data.begin(), data.end()), pref_constants::CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
449  std::size_t at = elem.find_last_of('@');
450  std::size_t eq = elem.find_first_of('=', at + 1);
451  if(at != std::string::npos && eq != std::string::npos) {
452  preferences::secure_buffer key(elem.begin() + eq + 1, elem.end());
453  credentials_.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), unescape(key));
454  }
455  }
456 }
457 
459 {
460  if(!remember_password()) {
462  return;
463  }
464 
465 #ifndef _WIN32
466  bool creds_file_existed = filesystem::file_exists(filesystem::get_credentials_file());
467 #endif
468 
469  preferences::secure_buffer credentials_data;
470  for(const auto& cred : credentials_) {
471  credentials_data.push_back(pref_constants::CREDENTIAL_SEPARATOR);
472  credentials_data.insert(credentials_data.end(), cred.username.begin(), cred.username.end());
473  credentials_data.push_back('@');
474  credentials_data.insert(credentials_data.end(), cred.server.begin(), cred.server.end());
475  credentials_data.push_back('=');
476  preferences::secure_buffer key_escaped = escape(cred.key);
477  credentials_data.insert(credentials_data.end(), key_escaped.begin(), key_escaped.end());
478  }
479  try {
481  preferences::secure_buffer encrypted = aes_encrypt(credentials_data, build_key("global", get_system_username()));
482  credentials_file->write(reinterpret_cast<const char*>(encrypted.data()), encrypted.size());
483  } catch(const filesystem::io_exception&) {
484  ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'";
485  }
486 
487 #ifndef _WIN32
488  if(!creds_file_existed) {
489  if(chmod(filesystem::get_credentials_file().c_str(), 0600) == -1) {
490  ERR_FS << "error setting permissions of credentials file '" << filesystem::get_credentials_file() << "'";
491  }
492  }
493 #endif
494 }
495 
496 //
497 // helpers
498 //
499 void prefs::set_child(const std::string& key, const config& val) {
501  preferences_.add_child(key, val);
502 }
503 
505 {
506  return preferences_.optional_child(key);
507 }
508 
509 std::string prefs::get(const std::string& key, const std::string& def) {
510  return preferences_[key].empty() ? def : preferences_[key];
511 }
512 
514 {
515  return preferences_[key];
516 }
517 
518 //
519 // accessors
520 //
521 static std::string fix_orb_color_name(const std::string& color) {
522  if (color.substr(0,4) == "orb_") {
523  if(color[4] >= '0' && color[4] <= '9') {
524  return color.substr(5);
525  } else {
526  return color.substr(4);
527  }
528  }
529  return color;
530 }
531 
532 std::string prefs::allied_color() {
533  std::string ally_color = preferences_[prefs_list::ally_orb_color].str();
534  if (ally_color.empty())
536  return fix_orb_color_name(ally_color);
537 }
538 void prefs::set_allied_color(const std::string& color_id) {
540 }
541 
542 std::string prefs::enemy_color() {
544  if (enemy_color.empty())
547 }
548 void prefs::set_enemy_color(const std::string& color_id) {
550 }
551 
552 std::string prefs::moved_color() {
554  if (moved_color.empty())
557 }
558 void prefs::set_moved_color(const std::string& color_id) {
560 }
561 
562 std::string prefs::unmoved_color() {
564  if (unmoved_color.empty())
567 }
568 void prefs::set_unmoved_color(const std::string& color_id) {
570 }
571 
572 std::string prefs::partial_color() {
573  std::string partmoved_color = preferences_[prefs_list::partial_orb_color].str();
574  if (partmoved_color.empty())
576  return fix_orb_color_name(partmoved_color);
577 }
578 void prefs::set_partial_color(const std::string& color_id) {
580 }
581 
583 {
584  const unsigned x_res = preferences_[prefs_list::xresolution].to_unsigned();
585  const unsigned y_res = preferences_[prefs_list::yresolution].to_unsigned();
586 
587  // Either resolution was unspecified, return default.
588  if(x_res == 0 || y_res == 0) {
590  }
591 
592  return point(
593  std::max<unsigned>(x_res, pref_constants::min_window_width),
594  std::max<unsigned>(y_res, pref_constants::min_window_height)
595  );
596 }
597 
598 void prefs::set_resolution(const point& res)
599 {
600  preferences_[prefs_list::xresolution] = std::to_string(res.x);
601  preferences_[prefs_list::yresolution] = std::to_string(res.y);
602 }
603 
605 {
606  // For now this has a minimum value of 1 and a maximum of 4.
607  return std::max<int>(std::min<int>(preferences_[prefs_list::pixel_scale].to_int(1), pref_constants::max_pixel_scale), pref_constants::min_pixel_scale);
608 }
609 
611 {
613 }
614 
616 {
617  if(video::headless()) {
618  return true;
619  }
620 
621  return preferences_[prefs_list::turbo].to_bool();
622 }
623 
624 void prefs::set_turbo(bool ison)
625 {
626  preferences_[prefs_list::turbo] = ison;
627 }
628 
630 {
631  // Clip at 80 because if it's too low it'll cause crashes
632  return std::max<int>(std::min<int>(preferences_[prefs_list::font_scale].to_int(100), pref_constants::max_font_scaling), pref_constants::min_font_scaling);
633 }
634 
636 {
638 }
639 
641 {
642  return (size * font_scaling()) / 100;
643 }
644 
646 {
647  return preferences_[prefs_list::keepalive_timeout].to_int(20);
648 }
649 
650 void prefs::keepalive_timeout(int seconds)
651 {
652  preferences_[prefs_list::keepalive_timeout] = std::abs(seconds);
653 }
654 
656 {
657  // Sounds don't sound good on Windows unless the buffer size is 4k,
658  // but this seems to cause crashes on other systems...
659  #ifdef _WIN32
660  const std::size_t buf_size = 4096;
661  #else
662  const std::size_t buf_size = 1024;
663  #endif
664 
665  return preferences_[prefs_list::sound_buffer_size].to_int(buf_size);
666 }
667 
668 void prefs::save_sound_buffer_size(const std::size_t size)
669 {
670  const std::string new_size = std::to_string(size);
671  if (preferences_[prefs_list::sound_buffer_size] == new_size)
672  return;
673 
674  preferences_[prefs_list::sound_buffer_size] = new_size;
675 
677 }
678 
680 {
681  return preferences_[prefs_list::music_volume].to_int(100);
682 }
683 
685 {
686  if(music_volume() == vol) {
687  return;
688  }
689 
692 }
693 
695 {
696  return preferences_[prefs_list::sound_volume].to_int(100);
697 }
698 
700 {
701  if(sound_volume() == vol) {
702  return;
703  }
704 
707 }
708 
710 {
711  return preferences_[prefs_list::bell_volume].to_int(100);
712 }
713 
715 {
716  if(bell_volume() == vol) {
717  return;
718  }
719 
722 }
723 
724 // old pref name had uppercase UI
726 {
728  return preferences_[prefs_list::ui_volume].to_int(100);
729  } else {
730  return preferences_["UI_volume"].to_int(100);
731  }
732 }
733 
734 void prefs::set_ui_volume(int vol)
735 {
736  if(ui_volume() == vol) {
737  return;
738  }
739 
742 }
743 
745 {
746  return preferences_[prefs_list::turn_bell].to_bool(true);
747 }
748 
749 bool prefs::set_turn_bell(bool ison)
750 {
751  if(!turn_bell() && ison) {
753  if(!music_on() && !sound() && !ui_sound_on()) {
754  if(!sound::init_sound()) {
756  return false;
757  }
758  }
759  } else if(turn_bell() && !ison) {
762  if(!music_on() && !sound() && !ui_sound_on())
764  }
765  return true;
766 }
767 
768 // old pref name had uppercase UI
770 {
771  if(preferences_.has_attribute(prefs_list::ui_sound)) {
772  return preferences_[prefs_list::ui_sound].to_bool(true);
773  } else {
774  return preferences_["UI_sound"].to_bool(true);
775  }
776 }
777 
778 bool prefs::set_ui_sound(bool ison)
779 {
780  if(!ui_sound_on() && ison) {
781  preferences_[prefs_list::ui_sound] = true;
782  if(!music_on() && !sound() && !turn_bell()) {
783  if(!sound::init_sound()) {
784  preferences_[prefs_list::ui_sound] = false;
785  return false;
786  }
787  }
788  } else if(ui_sound_on() && !ison) {
789  preferences_[prefs_list::ui_sound] = false;
791  if(!music_on() && !sound() && !turn_bell())
793  }
794  return true;
795 }
796 
798 {
799  return preferences_[prefs_list::message_bell].to_bool(true);
800 }
801 
803 {
804  return preferences_[prefs_list::sound].to_bool(true);
805 }
806 
807 bool prefs::set_sound(bool ison) {
808  if(!sound() && ison) {
810  if(!music_on() && !turn_bell() && !ui_sound_on()) {
811  if(!sound::init_sound()) {
813  return false;
814  }
815  }
816  } else if(sound() && !ison) {
819  if(!music_on() && !turn_bell() && !ui_sound_on())
821  }
822  return true;
823 }
824 
826 {
827  return preferences_[prefs_list::music].to_bool(true);
828 }
829 
830 bool prefs::set_music(bool ison) {
831  if(!music_on() && ison) {
832  preferences_[prefs_list::music] = true;
833  if(!sound() && !turn_bell() && !ui_sound_on()) {
834  if(!sound::init_sound()) {
835  preferences_[prefs_list::music] = false;
836  return false;
837  }
838  }
839  else
841  } else if(music_on() && !ison) {
842  preferences_[prefs_list::music] = false;
843  if(!sound() && !turn_bell() && !ui_sound_on())
845  else
847  }
848  return true;
849 }
850 
852 {
853  return std::clamp<int>(preferences_[prefs_list::scroll].to_int(50), 1, 100);
854 }
855 
856 void prefs::set_scroll_speed(const int new_speed)
857 {
858  preferences_[prefs_list::scroll] = new_speed;
859 }
860 
862 {
863  return preferences_[prefs_list::middle_click_scrolls].to_bool(true);
864 }
865 
867 {
868  return preferences_[prefs_list::scroll_threshold].to_int(10);
869 }
870 
872 {
873  return fps_;
874 }
875 
876 void prefs::set_show_fps(bool value)
877 {
878  fps_ = value;
879 }
880 
882 {
884 }
885 
887 {
889 }
890 
892 {
894  preferences_.clear_children("hotkey");
895 }
896 
897 void prefs::add_alias(const std::string &alias, const std::string &command)
898 {
899  config &alias_list = preferences_.child_or_add("alias");
900  alias_list[alias] = command;
901 }
902 
903 
905 {
906  return get_child(prefs_list::alias);
907 }
908 
909 unsigned int prefs::sample_rate()
910 {
911  return preferences_[prefs_list::sample_rate].to_int(44100);
912 }
913 
914 void prefs::save_sample_rate(const unsigned int rate)
915 {
916  if (sample_rate() == rate)
917  return;
918 
919  preferences_[prefs_list::sample_rate] = rate;
920 
921  // If audio is open, we have to re set sample rate
923 }
924 
926 {
927  return preferences_[prefs_list::confirm_load_save_from_different_version].to_bool(true);
928 }
929 
931 {
932  return preferences_[prefs_list::use_twelve_hour_clock_format].to_bool();
933 }
934 
936 {
937  return sort_order::get_enum(preferences_[prefs_list::addon_manager_saved_order_direction].to_int()).value_or(sort_order::type::none);
938 }
939 
941 {
942  preferences_[prefs_list::addon_manager_saved_order_direction] = sort_order::get_string(value);
943 }
944 
945 bool prefs::achievement(const std::string& content_for, const std::string& id)
946 {
947  for(config& ach : preferences_.child_range(prefs_list::achievements))
948  {
949  if(ach["content_for"].str() == content_for)
950  {
951  std::vector<std::string> ids = utils::split(ach["ids"]);
952  return std::find(ids.begin(), ids.end(), id) != ids.end();
953  }
954  }
955  return false;
956 }
957 
958 void prefs::set_achievement(const std::string& content_for, const std::string& id)
959 {
960  for(config& ach : preferences_.child_range(prefs_list::achievements))
961  {
962  // if achievements already exist for this content and the achievement has not already been set, add it
963  if(ach["content_for"].str() == content_for)
964  {
965  std::vector<std::string> ids = utils::split(ach["ids"]);
966 
967  if(ids.empty())
968  {
969  ach["ids"] = id;
970  }
971  else if(std::find(ids.begin(), ids.end(), id) == ids.end())
972  {
973  ach["ids"] = ach["ids"].str() + "," + id;
974  }
975  ach.remove_children("in_progress", [&id](config cfg){return cfg["id"].str() == id;});
976  return;
977  }
978  }
979 
980  // else no achievements have been set for this content yet
981  config ach;
982  ach["content_for"] = content_for;
983  ach["ids"] = id;
984  preferences_.add_child(prefs_list::achievements, ach);
985 }
986 
987 int prefs::progress_achievement(const std::string& content_for, const std::string& id, int limit, int max_progress, int amount)
988 {
989  if(achievement(content_for, id))
990  {
991  return -1;
992  }
993 
994  for(config& ach : preferences_.child_range(prefs_list::achievements))
995  {
996  // if achievements already exist for this content and the achievement has not already been set, add it
997  if(ach["content_for"].str() == content_for)
998  {
999  // check if this achievement has progressed before - if so then increment it
1000  for(config& in_progress : ach.child_range("in_progress"))
1001  {
1002  if(in_progress["id"].str() == id)
1003  {
1004  // don't let using 'limit' decrease the achievement's current progress
1005  int starting_progress = in_progress["progress_at"].to_int();
1006  if(starting_progress >= limit) {
1007  return starting_progress;
1008  }
1009 
1010  in_progress["progress_at"] = std::clamp(starting_progress + amount, 0, std::min(limit, max_progress));
1011  return in_progress["progress_at"].to_int();
1012  }
1013  }
1014 
1015  // else this is the first time this achievement is progressing
1016  if(amount != 0)
1017  {
1018  config set_progress;
1019  set_progress["id"] = id;
1020  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1021 
1022  config& child = ach.add_child("in_progress", set_progress);
1023  return child["progress_at"].to_int();
1024  }
1025  return 0;
1026  }
1027  }
1028 
1029  // else not only has this achievement not progressed before, this is the first achievement for this achievement group to be added
1030  if(amount != 0)
1031  {
1032  config ach;
1033  config set_progress;
1034 
1035  set_progress["id"] = id;
1036  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1037 
1038  ach["content_for"] = content_for;
1039  ach["ids"] = "";
1040 
1041  config& child = ach.add_child("in_progress", set_progress);
1042  preferences_.add_child(prefs_list::achievements, ach);
1043  return child["progress_at"].to_int();
1044  }
1045  return 0;
1046 }
1047 
1048 bool prefs::sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1049 {
1050  // this achievement is already completed
1051  if(achievement(content_for, id))
1052  {
1053  return true;
1054  }
1055 
1056  for(config& ach : preferences_.child_range(prefs_list::achievements))
1057  {
1058  if(ach["content_for"].str() == content_for)
1059  {
1060  // check if the specific sub-achievement has been completed but the overall achievement is not completed
1061  for(const auto& in_progress : ach.child_range("in_progress"))
1062  {
1063  if(in_progress["id"] == id)
1064  {
1065  std::vector<std::string> sub_ids = utils::split(in_progress["sub_ids"]);
1066  return std::find(sub_ids.begin(), sub_ids.end(), sub_id) != sub_ids.end();
1067  }
1068  }
1069  }
1070  }
1071  return false;
1072 }
1073 
1074 void prefs::set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1075 {
1076  // this achievement is already completed
1077  if(achievement(content_for, id))
1078  {
1079  return;
1080  }
1081 
1082  for(config& ach : preferences_.child_range(prefs_list::achievements))
1083  {
1084  // if achievements already exist for this content and the achievement has not already been set, add it
1085  if(ach["content_for"].str() == content_for)
1086  {
1087  // check if this achievement has had sub-achievements set before
1088  for(config& in_progress : ach.child_range("in_progress"))
1089  {
1090  if(in_progress["id"].str() == id)
1091  {
1092  std::vector<std::string> sub_ids = utils::split(ach["ids"]);
1093 
1094  if(std::find(sub_ids.begin(), sub_ids.end(), sub_id) == sub_ids.end())
1095  {
1096  in_progress["sub_ids"] = in_progress["sub_ids"].str() + "," + sub_id;
1097  }
1098 
1099  in_progress["progress_at"] = sub_ids.size()+1;
1100  return;
1101  }
1102  }
1103 
1104  // else if this is the first sub-achievement being set
1105  config set_progress;
1106  set_progress["id"] = id;
1107  set_progress["sub_ids"] = sub_id;
1108  set_progress["progress_at"] = 1;
1109  ach.add_child("in_progress", set_progress);
1110  return;
1111  }
1112  }
1113 
1114  // else not only has this achievement not had a sub-achievement completed before, this is the first achievement for this achievement group to be added
1115  config ach;
1116  config set_progress;
1117 
1118  set_progress["id"] = id;
1119  set_progress["sub_ids"] = sub_id;
1120  set_progress["progress_at"] = 1;
1121 
1122  ach["content_for"] = content_for;
1123  ach["ids"] = "";
1124 
1125  ach.add_child("in_progress", set_progress);
1126  preferences_.add_child(prefs_list::achievements, ach);
1127 }
1128 
1130 {
1131  return preferences_[prefs_list::show_deprecation].to_bool(def);
1132 }
1133 
1135 {
1136  return preferences_[prefs_list::scroll_when_mouse_outside].to_bool(def);
1137 }
1138 
1140 {
1141  set_child(prefs_list::dir_bookmarks, cfg);
1142 }
1144 {
1145  return get_child(prefs_list::dir_bookmarks);
1146 }
1147 
1149 {
1150  return preferences_[prefs_list::lobby_auto_open_whisper_windows].to_bool(true);
1151 }
1152 
1154 {
1155  return std::max(std::size_t(1), preferences_[prefs_list::editor_max_recent_files].to_size_t(10));
1156 }
1157 
1158 //
1159 // NOTE: The MRU read/save functions enforce the entry count limit in
1160 // order to ensure the list on disk doesn't grow forever. Otherwise,
1161 // normally this would be the UI's responsibility instead.
1162 //
1163 
1164 std::vector<std::string> prefs::do_read_editor_mru()
1165 {
1166  auto cfg = get_child(prefs_list::editor_recent_files);
1167 
1168  std::vector<std::string> mru;
1169  if(!cfg) {
1170  return mru;
1171  }
1172 
1173  for(const config& child : cfg->child_range("entry"))
1174  {
1175  const std::string& entry = child["path"].str();
1176  if(!entry.empty()) {
1177  mru.push_back(entry);
1178  }
1179  }
1180 
1181  mru.resize(std::min(editor_mru_limit(), mru.size()));
1182 
1183  return mru;
1184 }
1185 
1186 void prefs::do_commit_editor_mru(const std::vector<std::string>& mru)
1187 {
1188  config cfg;
1189  unsigned n = 0;
1190 
1191  for(const std::string& entry : mru)
1192  {
1193  if(entry.empty()) {
1194  continue;
1195  }
1196 
1197  config& child = cfg.add_child("entry");
1198  child["path"] = entry;
1199 
1200  if(++n >= editor_mru_limit()) {
1201  break;
1202  }
1203  }
1204 
1205  set_child(prefs_list::editor_recent_files, cfg);
1206 }
1207 
1208 std::vector<std::string> prefs::recent_files()
1209 {
1210  return do_read_editor_mru();
1211 }
1212 
1213 void prefs::add_recent_files_entry(const std::string& path)
1214 {
1215  if(path.empty()) {
1216  return;
1217  }
1218 
1219  std::vector<std::string> mru = do_read_editor_mru();
1220 
1221  // Enforce uniqueness. Normally shouldn't do a thing unless somebody
1222  // has been tampering with the preferences file.
1223  utils::erase(mru, path);
1224 
1225  mru.insert(mru.begin(), path);
1226  mru.resize(std::min(editor_mru_limit(), mru.size()));
1227 
1228  do_commit_editor_mru(mru);
1229 }
1230 
1232 {
1233  return preferences_[prefs_list::color_cursors].to_bool(true);
1234 }
1235 
1237 {
1238  preferences_[prefs_list::color_cursors] = value;
1239 
1240  cursor::set();
1241 }
1242 
1244 {
1245  return preferences_[prefs_list::unit_standing_animations].to_bool(true);
1246 }
1247 
1249 {
1250  preferences_[prefs_list::unit_standing_animations] = value;
1251 
1252  if(display* d = display::get_singleton()) {
1253  d->reset_standing_animations();
1254  }
1255 }
1256 
1258 {
1259  std::vector<theme_info> themes = theme::get_basic_theme_info();
1260 
1261  if (themes.empty()) {
1263  _("No known themes. Try changing from within an existing game."));
1264 
1265  return false;
1266  }
1267 
1268  gui2::dialogs::theme_list dlg(themes);
1269 
1270  for (std::size_t k = 0; k < themes.size(); ++k) {
1271  if(themes[k].id == theme()) {
1272  dlg.set_selected_index(static_cast<int>(k));
1273  }
1274  }
1275 
1276  dlg.show();
1277  const int action = dlg.selected_index();
1278 
1279  if (action >= 0) {
1280  set_theme(themes[action].id);
1281  if(display::get_singleton() && resources::gamedata && resources::gamedata->get_theme().empty()) {
1282  display::get_singleton()->set_theme(themes[action].id);
1283  }
1284 
1285  return true;
1286  }
1287 
1288  return false;
1289 }
1290 
1292 {
1293  const std::string filename = filesystem::get_wesnothd_name();
1294 
1295  const std::string& old_path = filesystem::directory_name(get_mp_server_program_name());
1296  std::string path =
1297  !old_path.empty() && filesystem::is_directory(old_path)
1298  ? old_path : filesystem::get_exe_dir();
1299 
1300  const std::string msg = VGETTEXT("The <b>$filename</b> server application provides multiplayer server functionality and is required for hosting local network games. It will normally be found in the same folder as the game executable.", {{"filename", filename}});
1301 
1303 
1304  dlg.set_title(_("Find Server Application"))
1305  .set_message(msg)
1306  .set_ok_label(_("Select"))
1307  .set_read_only(true)
1309  .set_path(path);
1310 
1311  if(dlg.show()) {
1312  path = dlg.path();
1314  }
1315 }
1316 
1317 std::string prefs::theme()
1318 {
1319  if(video::headless()) {
1320  static const std::string null_theme = "null";
1321  return null_theme;
1322  }
1323 
1324  std::string res = preferences_[prefs_list::theme];
1325  if(res.empty()) {
1326  return "Default";
1327  }
1328 
1329  return res;
1330 }
1331 
1332 void prefs::set_theme(const std::string& theme)
1333 {
1334  if(theme != "null") {
1335  preferences_[prefs_list::theme] = theme;
1336  }
1337 }
1338 
1339 void prefs::set_mp_server_program_name(const std::string& path)
1340 {
1341  if(path.empty()) {
1342  preferences_.remove_attribute(prefs_list::mp_server_program_name);
1343  } else {
1344  preferences_[prefs_list::mp_server_program_name] = path;
1345  }
1346 }
1347 
1349 {
1350  return preferences_[prefs_list::mp_server_program_name].str();
1351 }
1352 
1353 const std::map<std::string, preferences::acquaintance>& prefs::get_acquaintances()
1354 {
1355  return acquaintances_;
1356 }
1357 
1358 const std::string prefs::get_ignored_delim()
1359 {
1360  std::vector<std::string> ignored;
1361 
1362  for(const auto& person : acquaintances_) {
1363  if(person.second.get_status() == "ignore") {
1364  ignored.push_back(person.second.get_nick());
1365  }
1366  }
1367 
1368  return utils::join(ignored);
1369 }
1370 
1371 // returns acquaintances in the form nick => notes where the status = filter
1372 std::map<std::string, std::string> prefs::get_acquaintances_nice(const std::string& filter)
1373 {
1374  std::map<std::string, std::string> ac_nice;
1375 
1376  for(const auto& a : acquaintances_) {
1377  if(a.second.get_status() == filter) {
1378  ac_nice[a.second.get_nick()] = a.second.get_notes();
1379  }
1380  }
1381 
1382  return ac_nice;
1383 }
1384 
1385 std::pair<preferences::acquaintance*, bool> prefs::add_acquaintance(const std::string& nick, const std::string& mode, const std::string& notes)
1386 {
1387  if(!utils::isvalid_wildcard(nick)) {
1388  return std::pair(nullptr, false);
1389  }
1390 
1391  preferences::acquaintance new_entry(nick, mode, notes);
1392  auto [iter, added_new] = acquaintances_.insert_or_assign(nick, new_entry);
1393 
1394  return std::pair(&iter->second, added_new);
1395 }
1396 
1397 bool prefs::remove_acquaintance(const std::string& nick)
1398 {
1400 
1401  // nick might include the notes, depending on how we're removing
1402  if(i == acquaintances_.end()) {
1403  std::size_t pos = nick.find_first_of(' ');
1404 
1405  if(pos != std::string::npos) {
1406  i = acquaintances_.find(nick.substr(0, pos));
1407  }
1408  }
1409 
1410  if(i == acquaintances_.end()) {
1411  return false;
1412  }
1413 
1414  acquaintances_.erase(i);
1415 
1416  return true;
1417 }
1418 
1419 bool prefs::is_friend(const std::string& nick)
1420 {
1421  const auto it = acquaintances_.find(nick);
1422 
1423  if(it == acquaintances_.end()) {
1424  return false;
1425  } else {
1426  return it->second.get_status() == "friend";
1427  }
1428 }
1429 
1430 bool prefs::is_ignored(const std::string& nick)
1431 {
1432  const auto it = acquaintances_.find(nick);
1433 
1434  if(it == acquaintances_.end()) {
1435  return false;
1436  } else {
1437  return it->second.get_status() == "ignore";
1438  }
1439 }
1440 
1441 void prefs::add_completed_campaign(const std::string& campaign_id, const std::string& difficulty_level)
1442 {
1443  completed_campaigns_[campaign_id].insert(difficulty_level);
1444 }
1445 
1446 bool prefs::is_campaign_completed(const std::string& campaign_id)
1447 {
1448  return completed_campaigns_.count(campaign_id) != 0;
1449 }
1450 
1451 bool prefs::is_campaign_completed(const std::string& campaign_id, const std::string& difficulty_level)
1452 {
1453  const auto it = completed_campaigns_.find(campaign_id);
1454  return it == completed_campaigns_.end() ? false : it->second.count(difficulty_level) != 0;
1455 }
1456 
1457 bool prefs::parse_should_show_lobby_join(const std::string& sender, const std::string& message)
1458 {
1459  // If it's actually not a lobby join or leave message return true (show it).
1460  if(sender != "server") {
1461  return true;
1462  }
1463 
1464  std::string::size_type pos = message.find(" has logged into the lobby");
1465  if(pos == std::string::npos) {
1466  pos = message.find(" has disconnected");
1467  if(pos == std::string::npos) {
1468  return true;
1469  }
1470  }
1471 
1474  return false;
1475  }
1476 
1478  return true;
1479  }
1480 
1481  return is_friend(message.substr(0, pos));
1482 }
1483 
1485 {
1486  std::string pref = preferences_[prefs_list::lobby_joins];
1487  if(pref == "friends") {
1489  } else if(pref == "all") {
1491  } else if(pref == "none") {
1493  } else {
1495  }
1496 }
1497 
1499 {
1506  }
1507 }
1508 
1509 const std::vector<game_config::server_info>& prefs::builtin_servers_list()
1510 {
1511  static std::vector<game_config::server_info> pref_servers = game_config::server_list;
1512  return pref_servers;
1513 }
1514 
1515 std::vector<game_config::server_info> prefs::user_servers_list()
1516 {
1517  std::vector<game_config::server_info> pref_servers;
1518 
1519  for(const config& server : preferences_.child_range(prefs_list::server)) {
1520  pref_servers.emplace_back();
1521  pref_servers.back().name = server["name"].str();
1522  pref_servers.back().address = server["address"].str();
1523  }
1524 
1525  return pref_servers;
1526 }
1527 
1528 void prefs::set_user_servers_list(const std::vector<game_config::server_info>& value)
1529 {
1530  preferences_.clear_children(prefs_list::server);
1531 
1532  for(const auto& svinfo : value) {
1533  config& sv_cfg = preferences_.add_child(prefs_list::server);
1534  sv_cfg["name"] = svinfo.name;
1535  sv_cfg["address"] = svinfo.address;
1536  }
1537 }
1538 
1539 std::string prefs::network_host()
1540 {
1541  const std::string res = preferences_[prefs_list::host];
1542  if(res.empty()) {
1543  return builtin_servers_list().front().address;
1544  } else {
1545  return res;
1546  }
1547 }
1548 
1549 void prefs::set_network_host(const std::string& host)
1550 {
1551  preferences_[prefs_list::host] = host;
1552 }
1553 
1555 {
1556  if(!preferences_[prefs_list::campaign_server].empty()) {
1557  return preferences_[prefs_list::campaign_server].str();
1558  } else {
1560  }
1561 }
1562 
1563 void prefs::set_campaign_server(const std::string& host)
1564 {
1565  preferences_[prefs_list::campaign_server] = host;
1566 }
1567 
1569 {
1570  return preferences_[prefs_list::show_combat].to_bool(true);
1571 }
1572 
1574 {
1575  if(options_initialized_) {
1576  return option_values_;
1577  }
1578 
1579  if(!get_child(prefs_list::options)) {
1580  // It may be an invalid config, which would cause problems in
1581  // multiplayer_create, so let's replace it with an empty but valid
1582  // config
1584  } else {
1585  option_values_ = *get_child(prefs_list::options);
1586  }
1587 
1588  options_initialized_ = true;
1589 
1590  return option_values_;
1591 }
1592 
1593 void prefs::set_options(const config& values)
1594 {
1595  set_child(prefs_list::options, values);
1596  options_initialized_ = false;
1597 }
1598 
1599 std::chrono::seconds prefs::countdown_init_time()
1600 {
1601  return chrono::parse_duration(preferences_[prefs_list::mp_countdown_init_time], 240s);
1602 }
1603 
1604 void prefs::set_countdown_init_time(const std::chrono::seconds& value)
1605 {
1606  preferences_[prefs_list::mp_countdown_init_time] = std::clamp(value, 0s, 1500s);
1607 }
1608 
1610 {
1611  preferences_.remove_attribute(prefs_list::mp_countdown_init_time);
1612 }
1613 
1614 std::chrono::seconds prefs::countdown_reservoir_time()
1615 {
1616  return chrono::parse_duration(preferences_[prefs_list::mp_countdown_reservoir_time], 360s);
1617 }
1618 
1619 void prefs::set_countdown_reservoir_time(const std::chrono::seconds& value)
1620 {
1621  preferences_[prefs_list::mp_countdown_reservoir_time] = std::clamp(value, 30s, 1500s);
1622 }
1623 
1625 {
1626  preferences_.remove_attribute(prefs_list::mp_countdown_reservoir_time);
1627 }
1628 
1629 std::chrono::seconds prefs::countdown_turn_bonus()
1630 {
1631  return chrono::parse_duration(preferences_[prefs_list::mp_countdown_turn_bonus], 240s);
1632 }
1633 
1634 void prefs::set_countdown_turn_bonus(const std::chrono::seconds& value)
1635 {
1636  preferences_[prefs_list::mp_countdown_turn_bonus] = std::clamp(value, 0s, 300s);
1637 }
1638 
1640 {
1641  preferences_.remove_attribute(prefs_list::mp_countdown_turn_bonus);
1642 }
1643 
1644 std::chrono::seconds prefs::countdown_action_bonus()
1645 {
1646  return chrono::parse_duration(preferences_[prefs_list::mp_countdown_action_bonus], 0s);
1647 }
1648 
1649 void prefs::set_countdown_action_bonus(const std::chrono::seconds& value)
1650 {
1651  preferences_[prefs_list::mp_countdown_action_bonus] = std::clamp(value, 0s, 30s);
1652 }
1653 
1655 {
1656  preferences_.remove_attribute(prefs_list::mp_countdown_action_bonus);
1657 }
1658 
1659 std::chrono::minutes prefs::chat_message_aging()
1660 {
1661  return chrono::parse_duration(preferences_[prefs_list::chat_message_aging], 20min);
1662 }
1663 
1664 void prefs::set_chat_message_aging(const std::chrono::minutes& value)
1665 {
1666  preferences_[prefs_list::chat_message_aging] = value;
1667 }
1668 
1670 {
1671  return settings::get_village_gold(preferences_[prefs_list::mp_village_gold]);
1672 }
1673 
1675 {
1676  preferences_[prefs_list::mp_village_gold] = value;
1677 }
1678 
1680 {
1681  return settings::get_village_support(preferences_[prefs_list::mp_village_support]);
1682 }
1683 
1685 {
1686  preferences_[prefs_list::mp_village_support] = std::to_string(value);
1687 }
1688 
1690 {
1691  return settings::get_xp_modifier(preferences_[prefs_list::mp_xp_modifier]);
1692 }
1693 
1694 void prefs::set_xp_modifier(int value)
1695 {
1696  preferences_[prefs_list::mp_xp_modifier] = value;
1697 }
1698 
1699 const std::vector<std::string>& prefs::modifications(bool mp)
1700 {
1702  if(mp) {
1703  mp_modifications_ = utils::split(preferences_[prefs_list::mp_modifications].str(), ',');
1705  } else {
1706  sp_modifications_ = utils::split(preferences_[prefs_list::sp_modifications].str(), ',');
1708  }
1709  }
1710 
1712 }
1713 
1714 void prefs::set_modifications(const std::vector<std::string>& value, bool mp)
1715 {
1716  if(mp) {
1717  preferences_[prefs_list::mp_modifications] = utils::join(value, ",");
1719  } else {
1720  preferences_[prefs_list::sp_modifications] = utils::join(value, ",");
1722  }
1723 }
1724 
1726 {
1727  return message_private_on_;
1728 }
1729 
1731 {
1732  message_private_on_ = value;
1733 }
1734 
1736 {
1737  const std::string& choice = preferences_[prefs_list::compress_saves];
1738 
1739  // "yes" was used in 1.11.7 and earlier; the compress_saves
1740  // option used to be a toggle for gzip in those versions.
1741  if(choice.empty() || choice == "gzip" || choice == "yes") {
1743  } else if(choice == "bzip2") {
1745  } else if(choice == "none" || choice == "no") { // see above
1747  } /*else*/
1748 
1749  // In case the preferences file was created by a later version
1750  // supporting some algorithm we don't; although why would anyone
1751  // playing a game need more algorithms, really...
1753 }
1754 
1755 std::string prefs::get_chat_timestamp(const std::chrono::system_clock::time_point& t)
1756 {
1757  if(chat_timestamp()) {
1758  if(use_twelve_hour_clock_format() == false) {
1759  return chrono::format_local_timestamp(t, _("[%H:%M]")) + " ";
1760  } else {
1761  return chrono::format_local_timestamp(t, _("[%I:%M %p]")) + " ";
1762  }
1763  }
1764 
1765  return "";
1766 }
1767 
1768 std::set<std::string>& prefs::encountered_units()
1769 {
1770  return encountered_units_set_;
1771 }
1772 
1773 std::set<t_translation::terrain_code>& prefs::encountered_terrains()
1774 {
1776 }
1777 
1778 /**
1779  * Returns a pointer to the history vector associated with given id
1780  * making a new one if it doesn't exist.
1781  *
1782  * @todo FIXME only used for gui2. Could be used for the above histories.
1783  */
1784 std::vector<std::string>* prefs::get_history(const std::string& id)
1785 {
1786  return &history_map_[id];
1787 }
1788 
1790 {
1791  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1792  return confirmation == "green" || confirmation == "yes";
1793 }
1794 
1796 {
1797  return preferences_[prefs_list::confirm_end_turn] == "yellow";
1798 }
1799 
1801 {
1802  // This is very non-intrusive so it is on by default
1803  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1804  return confirmation == "no_moves" || confirmation.empty();
1805 }
1806 
1807 void prefs::encounter_recruitable_units(const std::vector<team>& teams)
1808 {
1809  for(const team& help_team : teams) {
1810  help_team.log_recruitable();
1811  encountered_units_set_.insert(help_team.recruits().begin(), help_team.recruits().end());
1812  }
1813 }
1814 
1816 {
1817  for(const auto& help_unit : units) {
1818  encountered_units_set_.insert(help_unit.type_id());
1819  }
1820 }
1821 
1822 void prefs::encounter_recallable_units(const std::vector<team>& teams)
1823 {
1824  for(const team& t : teams) {
1825  for(const unit_const_ptr u : t.recall_list()) {
1826  encountered_units_set_.insert(u->type_id());
1827  }
1828  }
1829 }
1830 
1832 {
1833  map.for_each_loc([&](const map_location& loc) {
1834  const t_translation::terrain_code terrain = map.get_terrain(loc);
1835  encountered_terrains().insert(terrain);
1837  encountered_terrains().insert(t);
1838  }
1839  });
1840 }
1841 
1843 {
1844  encounter_recruitable_units(gameboard_.teams());
1845  encounter_start_units(gameboard_.units());
1846  encounter_recallable_units(gameboard_.teams());
1847  encounter_map_terrain(gameboard_.map());
1848 }
1849 
1851 {
1852  preferences_.remove_attribute(prefs_list::player_joins_sound);
1853  preferences_.remove_attribute(prefs_list::player_joins_notif);
1854  preferences_.remove_attribute(prefs_list::player_joins_lobby);
1855  preferences_.remove_attribute(prefs_list::player_leaves_sound);
1856  preferences_.remove_attribute(prefs_list::player_leaves_notif);
1857  preferences_.remove_attribute(prefs_list::player_leaves_lobby);
1858  preferences_.remove_attribute(prefs_list::private_message_sound);
1859  preferences_.remove_attribute(prefs_list::private_message_notif);
1860  preferences_.remove_attribute(prefs_list::private_message_lobby);
1861  preferences_.remove_attribute(prefs_list::friend_message_sound);
1862  preferences_.remove_attribute(prefs_list::friend_message_notif);
1863  preferences_.remove_attribute(prefs_list::friend_message_lobby);
1864  preferences_.remove_attribute(prefs_list::public_message_sound);
1865  preferences_.remove_attribute(prefs_list::public_message_notif);
1866  preferences_.remove_attribute(prefs_list::public_message_lobby);
1867  preferences_.remove_attribute(prefs_list::server_message_sound);
1868  preferences_.remove_attribute(prefs_list::server_message_notif);
1869  preferences_.remove_attribute(prefs_list::server_message_lobby);
1870  preferences_.remove_attribute(prefs_list::ready_for_start_sound);
1871  preferences_.remove_attribute(prefs_list::ready_for_start_notif);
1872  preferences_.remove_attribute(prefs_list::ready_for_start_lobby);
1873  preferences_.remove_attribute(prefs_list::game_has_begun_sound);
1874  preferences_.remove_attribute(prefs_list::game_has_begun_notif);
1875  preferences_.remove_attribute(prefs_list::game_has_begun_lobby);
1876  preferences_.remove_attribute(prefs_list::turn_changed_sound);
1877  preferences_.remove_attribute(prefs_list::turn_changed_notif);
1878  preferences_.remove_attribute(prefs_list::turn_changed_lobby);
1879  preferences_.remove_attribute(prefs_list::game_created_sound);
1880  preferences_.remove_attribute(prefs_list::game_created_notif);
1881  preferences_.remove_attribute(prefs_list::game_created_lobby);
1882 }
1883 
1885 {
1886 #ifdef _WIN32
1887  wchar_t buffer[300];
1888  DWORD size = 300;
1889  if(GetUserNameW(buffer, &size)) {
1890  //size includes a terminating null character.
1891  assert(size > 0);
1892  return unicode_cast<std::string>(std::wstring_view{buffer});
1893  }
1894 #else
1895  if(char* const login = getenv("USER")) {
1896  return login;
1897  }
1898 #endif
1899  return {};
1900 }
1901 
1902 preferences::secure_buffer prefs::build_key(const std::string& server, const std::string& login)
1903 {
1904  std::string sysname = get_system_username();
1905  preferences::secure_buffer result(std::max<std::size_t>(server.size() + login.size() + sysname.size(), 32));
1906  unsigned char i = 0;
1907  std::generate(result.begin(), result.end(), [&i]() {return 'x' ^ i++;});
1908  std::copy(login.begin(), login.end(), result.begin());
1909  std::copy(sysname.begin(), sysname.end(), result.begin() + login.size());
1910  std::copy(server.begin(), server.end(), result.begin() + login.size() + sysname.size());
1911  return result;
1912 }
1913 
1915 {
1916 #ifndef __APPLE__
1917  int update_length;
1918  int extra_length;
1919  int total_length;
1920  // AES IV is generally 128 bits
1921  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1922  unsigned char encrypted_buffer[1024];
1923 
1924  if(plaintext.size() > 1008)
1925  {
1926  ERR_CFG << "Cannot encrypt data larger than 1008 bytes.";
1927  return preferences::secure_buffer();
1928  }
1929  DBG_CFG << "Encrypting data with length: " << plaintext.size();
1930 
1931  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1932  if(!ctx)
1933  {
1934  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
1935  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
1936  return preferences::secure_buffer();
1937  }
1938 
1939  // TODO: use EVP_EncryptInit_ex2 once openssl 3.0 is more widespread
1940  if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv) != 1)
1941  {
1942  ERR_CFG << "AES EVP_EncryptInit_ex failed with error:";
1943  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
1944  EVP_CIPHER_CTX_free(ctx);
1945  return preferences::secure_buffer();
1946  }
1947 
1948  if(EVP_EncryptUpdate(ctx, encrypted_buffer, &update_length, plaintext.data(), plaintext.size()) != 1)
1949  {
1950  ERR_CFG << "AES EVP_EncryptUpdate failed with error:";
1951  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
1952  EVP_CIPHER_CTX_free(ctx);
1953  return preferences::secure_buffer();
1954  }
1955  DBG_CFG << "Update length: " << update_length;
1956 
1957  if(EVP_EncryptFinal_ex(ctx, encrypted_buffer + update_length, &extra_length) != 1)
1958  {
1959  ERR_CFG << "AES EVP_EncryptFinal failed with error:";
1960  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
1961  EVP_CIPHER_CTX_free(ctx);
1962  return preferences::secure_buffer();
1963  }
1964  DBG_CFG << "Extra length: " << extra_length;
1965 
1966  EVP_CIPHER_CTX_free(ctx);
1967 
1968  total_length = update_length+extra_length;
1970  for(int i = 0; i < total_length; i++)
1971  {
1972  result.push_back(encrypted_buffer[i]);
1973  }
1974 
1975  DBG_CFG << "Successfully encrypted plaintext value of '" << utils::join(plaintext, "") << "' having length " << plaintext.size();
1976  DBG_CFG << "For a total encrypted length of: " << total_length;
1977 
1978  return result;
1979 #else
1980  size_t outWritten = 0;
1981  preferences::secure_buffer result(plaintext.size(), '\0');
1982 
1983  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
1984  kCCAlgorithmRC4,
1985  kCCOptionPKCS7Padding,
1986  key.data(),
1987  key.size(),
1988  nullptr,
1989  plaintext.data(),
1990  plaintext.size(),
1991  result.data(),
1992  result.size(),
1993  &outWritten);
1994 
1995  assert(ccStatus == kCCSuccess);
1996  assert(outWritten == plaintext.size());
1997 
1998  return result;
1999 #endif
2000 }
2001 
2003 {
2004 #ifndef __APPLE__
2005  int update_length;
2006  int extra_length;
2007  int total_length;
2008  // AES IV is generally 128 bits
2009  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
2010  unsigned char plaintext_buffer[1024];
2011 
2012  if(encrypted.size() > 1024)
2013  {
2014  ERR_CFG << "Cannot decrypt data larger than 1024 bytes.";
2015  return preferences::secure_buffer();
2016  }
2017  DBG_CFG << "Decrypting data with length: " << encrypted.size();
2018 
2019  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
2020  if(!ctx)
2021  {
2022  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
2023  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2024  return preferences::secure_buffer();
2025  }
2026 
2027  // TODO: use EVP_DecryptInit_ex2 once openssl 3.0 is more widespread
2028  if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv) != 1)
2029  {
2030  ERR_CFG << "AES EVP_DecryptInit_ex failed with error:";
2031  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2032  EVP_CIPHER_CTX_free(ctx);
2033  return preferences::secure_buffer();
2034  }
2035 
2036  if(EVP_DecryptUpdate(ctx, plaintext_buffer, &update_length, encrypted.data(), encrypted.size()) != 1)
2037  {
2038  ERR_CFG << "AES EVP_DecryptUpdate failed with error:";
2039  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2040  EVP_CIPHER_CTX_free(ctx);
2041  return preferences::secure_buffer();
2042  }
2043  DBG_CFG << "Update length: " << update_length;
2044 
2045  if(EVP_DecryptFinal_ex(ctx, plaintext_buffer + update_length, &extra_length) != 1)
2046  {
2047  ERR_CFG << "AES EVP_DecryptFinal failed with error:";
2048  ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2049  EVP_CIPHER_CTX_free(ctx);
2050  return preferences::secure_buffer();
2051  }
2052  DBG_CFG << "Extra length: " << extra_length;
2053 
2054  EVP_CIPHER_CTX_free(ctx);
2055 
2056  total_length = update_length+extra_length;
2058  for(int i = 0; i < total_length; i++)
2059  {
2060  result.push_back(plaintext_buffer[i]);
2061  }
2062 
2063  DBG_CFG << "Successfully decrypted data to the value: " << utils::join(result, "");
2064  DBG_CFG << "For a total decrypted length of: " << total_length;
2065 
2066  return result;
2067 #else
2068  size_t outWritten = 0;
2069  preferences::secure_buffer result(encrypted.size(), '\0');
2070 
2071  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
2072  kCCAlgorithmRC4,
2073  kCCOptionPKCS7Padding,
2074  key.data(),
2075  key.size(),
2076  nullptr,
2077  encrypted.data(),
2078  encrypted.size(),
2079  result.data(),
2080  result.size(),
2081  &outWritten);
2082 
2083  assert(ccStatus == kCCSuccess);
2084  assert(outWritten == encrypted.size());
2085 
2086  // the decrypted result is likely shorter than the encrypted data, so the extra padding needs to be removed.
2087  while(!result.empty() && result.back() == 0) {
2088  result.pop_back();
2089  }
2090 
2091  return result;
2092 #endif
2093 }
2094 
2096 {
2097  preferences::secure_buffer unescaped;
2098  unescaped.reserve(text.size());
2099  bool escaping = false;
2100  for(char c : text) {
2101  if(escaping) {
2102  if(c == '\xa') {
2103  unescaped.push_back('\xc');
2104  } else if(c == '.') {
2105  unescaped.push_back('@');
2106  } else {
2107  unescaped.push_back(c);
2108  }
2109  escaping = false;
2110  } else if(c == '\x1') {
2111  escaping = true;
2112  } else {
2113  unescaped.push_back(c);
2114  }
2115  }
2116  assert(!escaping);
2117  return unescaped;
2118 }
2119 
2121 {
2123  escaped.reserve(text.size());
2124  for(char c : text) {
2125  if(c == '\x1') {
2126  escaped.push_back('\x1');
2127  escaped.push_back('\x1');
2128  } else if(c == '\xc') {
2129  escaped.push_back('\x1');
2130  escaped.push_back('\xa');
2131  } else if(c == '@') {
2132  escaped.push_back('\x1');
2133  escaped.push_back('.');
2134  } else {
2135  escaped.push_back(c);
2136  }
2137  }
2138  return escaped;
2139 }
2140 
2142 {
2143  return preferences_[prefs_list::remember_password].to_bool();
2144 }
2145 
2146 void prefs::set_remember_password(bool remember)
2147 {
2148  preferences_[prefs_list::remember_password] = remember;
2149 
2150  if(remember) {
2151  load_credentials();
2152  } else {
2154  }
2155 }
2156 
2157 std::string prefs::login()
2158 {
2159  std::string name = get("login", pref_constants::EMPTY_LOGIN);
2160  if(name == pref_constants::EMPTY_LOGIN) {
2161  name = get_system_username();
2162  } else if(name.size() > 2 && name.front() == '@' && name.back() == '@') {
2163  name = name.substr(1, name.size() - 2);
2164  } else {
2165  ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)";
2166  }
2167  if(name.empty()) {
2168  return "player";
2169  }
2170  return name;
2171 }
2172 
2173 void prefs::set_login(const std::string& login)
2174 {
2175  auto login_clean = login;
2176  boost::trim(login_clean);
2177 
2178  preferences_[prefs_list::login] = '@' + login_clean + '@';
2179 }
2180 
2181 std::string prefs::password(const std::string& server, const std::string& login)
2182 {
2183  DBG_CFG << "Retrieving password for server: '" << server << "', login: '" << login << "'";
2184  auto login_clean = login;
2185  boost::trim(login_clean);
2186 
2187  if(!remember_password()) {
2188  if(!credentials_.empty() && credentials_[0].username == login_clean && credentials_[0].server == server) {
2189  auto temp = aes_decrypt(credentials_[0].key, build_key(server, login_clean));
2190  return std::string(temp.begin(), temp.end());
2191  } else {
2192  return "";
2193  }
2194  }
2195  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2196  return cred.server == server && cred.username == login_clean;
2197  });
2198  if(cred == credentials_.end()) {
2199  return "";
2200  }
2201  auto temp = aes_decrypt(cred->key, build_key(server, login_clean));
2202  return std::string(temp.begin(), temp.end());
2203 }
2204 
2205 void prefs::set_password(const std::string& server, const std::string& login, const std::string& key)
2206 {
2207  DBG_CFG << "Setting password for server: '" << server << "', login: '" << login << "'";
2208  auto login_clean = login;
2209  boost::trim(login_clean);
2210 
2211  preferences::secure_buffer temp(key.begin(), key.end());
2212  if(!remember_password()) {
2214  credentials_.emplace_back(login_clean, server, aes_encrypt(temp, build_key(server, login_clean)));
2215  return;
2216  }
2217  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2218  return cred.server == server && cred.username == login_clean;
2219  });
2220  if(cred == credentials_.end()) {
2221  // This is equivalent to emplace_back, but also returns the iterator to the new element
2222  cred = credentials_.emplace(credentials_.end(), login_clean, server);
2223  }
2224  cred->key = aes_encrypt(temp, build_key(server, login_clean));
2225 }
map_location loc
Definition: move.cpp:172
double t
Definition: astarsearch.cpp:63
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
const_attr_itors attribute_range() const
Definition: config.cpp:760
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
void clear_children(T... keys)
Definition: config.hpp:616
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1123
child_itors child_range(config_key_type key)
Definition: config.cpp:272
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:405
void remove_attribute(config_key_type key)
Definition: config.cpp:162
bool empty() const
Definition: config.cpp:849
void clear()
Definition: config.cpp:828
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
config & add_child(config_key_type key)
Definition: config.cpp:440
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:97
void set_theme(const std::string &new_theme)
Definition: display.cpp:248
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:111
Game board class.
Definition: game_board.hpp:47
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static game_config_view wrap(const config &cfg)
config_array_view child_range(config_key_type key) const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
void for_each_loc(const F &f) const
Definition: map.hpp:136
Encapsulates the map of the game.
Definition: map.hpp:172
const t_translation::ter_list & underlying_union_terrain(const map_location &loc) const
Definition: map.cpp:59
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:59
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
file_dialog & set_filename(const std::string &value)
Sets the initial file name input but not the path.
file_dialog & set_message(const std::string &value)
Sets the current dialog instructions/message text.
Definition: file_dialog.hpp:78
bool show(const unsigned auto_close_time=0)
Shows the window.
int selected_index() const
Returns the selected item index after displaying.
Definition: theme_list.hpp:35
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
Definition: theme_list.hpp:41
const std::string & get_nick() const
Definition: preferences.hpp:91
void set_lobby_joins(pref_constants::lobby_joins show)
std::string enemy_color()
config::attribute_value get_as_attribute(const std::string &key)
void set_network_host(const std::string &host)
std::set< t_translation::terrain_code > & encountered_terrains()
const config & options()
bool set_music(bool ison)
void set_sound_volume(int vol)
void set_remember_password(bool remember)
const std::map< std::string, preferences::acquaintance > & get_acquaintances()
optional_const_config get_alias()
bool show_theme_dialog()
void set_turbo(bool ison)
std::string get_system_username()
bool mp_modifications_initialized_
std::map< std::string, std::vector< std::string > > history_map_
void save_hotkeys()
int village_support()
void set_addon_manager_saved_order_direction(sort_order::type value)
std::chrono::seconds countdown_turn_bonus()
bool middle_click_scrolls()
bool turn_bell()
void set_countdown_reservoir_time(const std::chrono::seconds &value)
std::set< std::string > & encountered_units()
void add_alias(const std::string &alias, const std::string &command)
void write_preferences()
void clear_preferences()
std::vector< std::string > sp_modifications_
void clear_mp_alert_prefs()
The most recently selected add-on id from the editor.
void set_village_gold(int value)
bool confirm_load_save_from_different_version()
std::string network_host()
void clear_countdown_init_time()
std::set< std::string > unknown_unsynced_attributes_
bool set_ui_sound(bool ison)
std::chrono::minutes chat_message_aging()
void set_login(const std::string &login)
static prefs & get()
const std::string get_ignored_delim()
std::map< std::string, preferences::acquaintance > acquaintances_
static constexpr std::array unsynced_children_
bool fps_
int scroll_speed()
bool message_private()
void encounter_recallable_units(const std::vector< team > &teams)
std::map< std::string, std::set< std::string > > completed_campaigns_
bool show_combat()
std::string theme()
optional_const_config dir_bookmarks()
void set_theme(const std::string &theme)
void set_xp_modifier(int value)
void set_user_servers_list(const std::vector< game_config::server_info > &value)
preferences::secure_buffer build_key(const std::string &server, const std::string &login)
Fills a secure_buffer with 32 bytes of deterministically generated bytes, then overwrites it with the...
bool sound()
bool is_campaign_completed(const std::string &campaign_id)
void set_allied_color(const std::string &color_id)
bool get_scroll_when_mouse_outside(bool def)
void show_wesnothd_server_search()
bool green_confirm()
int bell_volume()
int mouse_scroll_threshold()
Gets the threshold for when to scroll.
std::string get_chat_timestamp(const std::chrono::system_clock::time_point &t)
bool is_ignored(const std::string &nick)
std::vector< std::string > mp_modifications_
bool achievement(const std::string &content_for, const std::string &id)
std::set< std::string > unknown_synced_attributes_
void set_enemy_color(const std::string &color_id)
void save_credentials()
std::chrono::seconds countdown_init_time()
void set_password(const std::string &server, const std::string &login, const std::string &key)
bool sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
void clear_credentials()
std::vector< preferences::login_info > credentials_
std::vector< std::string > do_read_editor_mru()
void encounter_recruitable_units(const std::vector< team > &teams)
bool parse_should_show_lobby_join(const std::string &sender, const std::string &message)
int progress_achievement(const std::string &content_for, const std::string &id, int limit=999999, int max_progress=999999, int amount=0)
Increments the achievement's current progress by amount if it hasn't already been completed.
sort_order::type addon_manager_saved_order_direction()
bool set_turn_bell(bool ison)
static constexpr std::array synced_attributes_
unsigned int sample_rate()
void set_countdown_turn_bonus(const std::chrono::seconds &value)
static constexpr std::array synced_children_
void set_options(const config &values)
void set_message_private(bool value)
std::vector< game_config::server_info > user_servers_list()
optional_const_config get_child(const std::string &key)
void load_credentials()
std::string unmoved_color()
void set_music_volume(int vol)
void load_advanced_prefs(const game_config_view &gc)
std::string allied_color()
void set_show_standing_animations(bool value)
void set_show_fps(bool value)
void set_pixel_scale(const int scale)
std::size_t editor_mru_limit()
void set_color_cursors(bool value)
std::vector< preferences::option > advanced_prefs_
void encounter_start_units(const unit_map &units)
pref_constants::lobby_joins get_lobby_joins()
bool show_fps()
void load_hotkeys()
void set_chat_message_aging(const std::chrono::minutes &value)
void encounter_map_terrain(const gamemap &map)
int font_scaling()
void set_village_support(int value)
preferences::secure_buffer escape(const preferences::secure_buffer &text)
std::chrono::seconds countdown_action_bonus()
std::string moved_color()
bool confirm_no_moves()
void set_font_scaling(int scale)
std::size_t sound_buffer_size()
bool use_color_cursors()
bool yellow_confirm()
config option_values_
void migrate_preferences(const std::string &prefs_dir)
bool message_bell()
void set_dir_bookmarks(const config &cfg)
bool message_private_on_
std::set< t_translation::terrain_code > encountered_terrains_set_
bool set_sound(bool ison)
bool is_friend(const std::string &nick)
void set_achievement(const std::string &content_for, const std::string &id)
Marks the specified achievement as completed.
preferences::secure_buffer aes_decrypt(const preferences::secure_buffer &text, const preferences::secure_buffer &key)
Same as aes_encrypt(), except of course it takes encrypted data as an argument and returns decrypted ...
int village_gold()
config preferences_
void clear_countdown_turn_bonus()
std::string login()
bool music_on()
int font_scaled(int size)
void set_scroll_speed(const int scroll)
std::pair< preferences::acquaintance *, bool > add_acquaintance(const std::string &nick, const std::string &mode, const std::string &notes)
bool ui_sound_on()
bool options_initialized_
int music_volume()
int pixel_scale()
std::string partial_color()
preferences::secure_buffer unescape(const preferences::secure_buffer &text)
void set_resolution(const point &res)
void set_campaign_server(const std::string &host)
void set_unmoved_color(const std::string &color_id)
bool remove_acquaintance(const std::string &nick)
void clear_countdown_reservoir_time()
void set_ui_volume(int vol)
bool turbo()
const std::vector< std::string > & modifications(bool mp=true)
void do_commit_editor_mru(const std::vector< std::string > &mru)
std::chrono::seconds countdown_reservoir_time()
compression::format save_compression_format()
std::vector< std::string > recent_files()
Retrieves the list of recently opened files.
std::set< std::string > encountered_units_set_
void set_bell_volume(int vol)
std::string get_mp_server_program_name()
void set_partial_color(const std::string &color_id)
void set_child(const std::string &key, const config &val)
static constexpr std::array unsynced_attributes_
std::set< std::string > all_attributes()
int keepalive_timeout()
void set_mp_server_program_name(const std::string &)
bool remember_password()
int ui_volume()
void add_recent_files_entry(const std::string &path)
Adds an entry to the recent files list.
void set_moved_color(const std::string &color_id)
point resolution()
void save_sample_rate(const unsigned int rate)
bool use_twelve_hour_clock_format()
static bool no_preferences_save
int sound_volume()
void set_modifications(const std::vector< std::string > &value, bool mp=true)
void set_countdown_init_time(const std::chrono::seconds &value)
void set_sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
Marks the specified sub-achievement as completed.
std::set< std::string > unknown_unsynced_children_
const std::vector< game_config::server_info > & builtin_servers_list()
void reload_preferences()
std::string campaign_server()
std::set< std::string > unknown_synced_children_
void add_completed_campaign(const std::string &campaign_id, const std::string &difficulty_level)
void clear_hotkeys()
void save_sound_buffer_size(const std::size_t size)
int xp_modifier()
void encounter_all_content(const game_board &gb)
std::string password(const std::string &server, const std::string &login)
bool sp_modifications_initialized_
std::vector< std::string > * get_history(const std::string &id)
Returns a pointer to the history vector associated with given id making a new one if it doesn't exist...
preferences::secure_buffer aes_encrypt(const preferences::secure_buffer &text, const preferences::secure_buffer &key)
Encrypts the value of text using key and a hard coded IV using AES.
void set_countdown_action_bonus(const std::chrono::seconds &value)
bool auto_open_whisper_windows()
void load_preferences()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
bool get_show_deprecation(bool def)
bool show_standing_animations()
void clear_countdown_action_bonus()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
Definition: theme.hpp:44
static std::vector< theme_info > get_basic_theme_info(bool include_hidden=false)
Returns minimal info about saved themes, optionally including hidden ones.
Definition: theme.cpp:987
Container associating units to locations.
Definition: map.hpp:98
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1029
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
Standard logging facilities (interface).
General settings and defaults for scenarios.
auto parse_duration(const config_attribute_value &val, const Duration &def=Duration{0})
Definition: chrono.hpp:70
auto format_local_timestamp(const std::chrono::system_clock::time_point &time, std::string_view format="%F %T")
Definition: chrono.hpp:61
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:50
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:326
std::string get_exe_dir()
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::string get_synced_prefs_file()
location of preferences file containing preferences that are synced between computers note that wesno...
std::string get_unsynced_prefs_file()
location of preferences file containing preferences that aren't synced between computers
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:53
std::string get_credentials_file()
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:54
std::string get_wesnothd_name()
std::string get_default_prefs_file()
std::string partial_orb_color
std::string moved_orb_color
std::string unmoved_orb_color
std::string ally_orb_color
std::string enemy_orb_color
std::string turn_bell
std::string path
Definition: filesystem.cpp:91
const version_info wesnoth_version(VERSION)
std::vector< server_info > server_list
Definition: game_config.cpp:76
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:64
static int bell_volume()
static int music_volume()
static bool sound()
static int ui_volume()
static int sound_volume()
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
void save_hotkeys(config &cfg)
Save the non-default hotkeys to the config.
void reset_default_hotkeys()
Reset all hotkeys to the defaults.
void load_custom_hotkeys(const game_config_view &cfg)
Registers all hotkeys present in this config, overwriting any matching default hotkeys.
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:50
const int min_window_height
Definition: preferences.hpp:37
const int max_pixel_scale
Definition: preferences.hpp:49
const std::string EMPTY_LOGIN
Definition: preferences.hpp:57
const int min_pixel_scale
Definition: preferences.hpp:48
const std::string default_addons_server
Definition: preferences.hpp:61
const int def_window_width
Definition: preferences.hpp:39
const int min_font_scaling
Definition: preferences.hpp:45
const int min_window_width
Definition: preferences.hpp:36
const int max_font_scaling
Definition: preferences.hpp:46
const int def_window_height
Definition: preferences.hpp:40
const unsigned char CREDENTIAL_SEPARATOR
Definition: preferences.hpp:56
game_data * gamedata
Definition: resources.cpp:22
static std::string at(const std::string &file, int line)
int get_village_support(const std::string &value)
Gets the village unit level support.
int get_xp_modifier(const std::string &value)
Gets the xp modifier.
int get_village_gold(const std::string &value, const game_classification *classification)
Gets the village gold.
void set_bell_volume(int vol)
Definition: sound.cpp:1134
void reset_sound()
Definition: sound.cpp:527
bool init_sound()
Definition: sound.cpp:444
void close_sound()
Definition: sound.cpp:496
void play_music()
Definition: sound.cpp:618
void stop_music()
Definition: sound.cpp:558
void stop_UI_sound()
Definition: sound.cpp:593
void stop_bell()
Definition: sound.cpp:581
void set_music_volume(int vol)
Definition: sound.cpp:1094
void stop_sound()
Definition: sound.cpp:566
void set_UI_volume(int vol)
Definition: sound.cpp:1146
void set_sound_volume(int vol)
Definition: sound.cpp:1114
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
std::string write_list(const ter_list &list)
Writes a list of terrains to a string, only writes the new format.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
@ REMOVE_EMPTY
void trim(std::string_view &s)
bool isvalid_wildcard(const std::string &username)
Check if the username pattern contains only valid characters.
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:117
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
bool headless()
The game is running headless.
Definition: video.cpp:139
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, ColorFormat colFmt, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition: xbrz.cpp:1170
std::string_view data
Definition: picture.cpp:178
static lg::log_domain log_filesystem("filesystem")
#define ERR_CFG
Definition: preferences.cpp:61
#define DBG_CFG
Definition: preferences.cpp:62
#define ERR_ADV
Definition: preferences.cpp:68
static lg::log_domain advanced_preferences("advanced_preferences")
static std::string fix_orb_color_name(const std::string &color)
#define ERR_FS
Definition: preferences.cpp:65
static lg::log_domain log_config("config")
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:629
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:766
std::string filename
Filename.
An exception object used when an IO error occurs.
Definition: filesystem.hpp:67
Encapsulates the map of the game.
Definition: location.hpp:45
Holds a 2D point.
Definition: point.hpp:25
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
mock_char c
static map_location::direction n
static map_location::direction s
#define d
#define e