The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
preferences.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2024 - 2025
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"
28#include "game_config.hpp"
29#include "game_data.hpp"
30#include "gettext.hpp"
35#include "log.hpp"
36#include "map_settings.hpp"
37#include "map/map.hpp"
38#include "resources.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
60static 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
64static lg::log_domain log_filesystem("filesystem");
65#define ERR_FS LOG_STREAM(err, log_filesystem)
66
67static lg::log_domain advanced_preferences("advanced_preferences");
68#define ERR_ADV LOG_STREAM(err, advanced_preferences)
69
70using 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
151
152 try {
155 }
156 } catch (...) {
157 ERR_FS << "Failed to write preferences due to exception: " << utils::get_unknown_exception_type();
158 }
159}
160
162{
163 advanced_prefs_.clear();
164
165 for(const config& pref : gc.child_range("advanced_preference")) {
166 try {
167 advanced_prefs_.emplace_back(pref);
168 } catch(const std::invalid_argument& e) {
169 ERR_ADV << e.what();
170 continue;
171 }
172 }
173
174 // show_deprecation has a different default on the dev branch
175 if(game_config::wesnoth_version.is_dev_version()) {
177 if(op.field == prefs_list::show_deprecation) {
178 op.cfg["default"] = true;
179 }
180 }
181 }
182
183 std::sort(advanced_prefs_.begin(), advanced_prefs_.end(), [](const auto& lhs, const auto& rhs) { return translation::icompare(lhs.name, rhs.name) < 0; });
184}
185
186void prefs::migrate_preferences(const std::string& migrate_prefs_file)
187{
188 if(migrate_prefs_file != filesystem::get_synced_prefs_file() && filesystem::file_exists(migrate_prefs_file)) {
189 // if the file doesn't exist, just copy the file over
190 // else need to merge the preferences file
193 } else {
194 auto current_stream = filesystem::istream_file(filesystem::get_synced_prefs_file(), false);
195 config current_cfg = io::read(*current_stream);
196
197 auto old_stream = filesystem::istream_file(migrate_prefs_file, false);
198 config old_cfg = io::read(*old_stream);
199
200 // when both files have the same attribute, use the one from whichever was most recently modified
201 bool current_prefs_are_older = filesystem::file_modified_time(filesystem::get_synced_prefs_file()) < filesystem::file_modified_time(migrate_prefs_file);
202 for(const auto& [key, value] : old_cfg.attribute_range()) {
203 if(current_prefs_are_older || !current_cfg.has_attribute(key)) {
204 preferences_[key] = value;
205 }
206 }
207
208 // don't touch child tags
209
211 }
212 }
213}
214
215// TODO: remove after 1.20. Keeps pre-1.20 profiles (which lack the pref) on "Default RNG" to keep old players happy.
217{
218 if(!preferences_.has_attribute(prefs_list::campaign_rng_mode)) {
219 preferences_[prefs_list::campaign_rng_mode] = "default";
221 }
222}
223
225{
226 if(video::headless()) {
227 return;
228 }
229
230 if(!preferences_.has_attribute(prefs_list::gui2_theme)) {
231 const point window_size = video::current_resolution();
232
233 // TODO: theme specific minimum/maximum sizes specified via [gui]
234 const point min_size{
237 };
238
239 if(min_size.x <= window_size.x && min_size.y <= window_size.y) {
240 preferences_[prefs_list::gui2_theme] = "modern";
241 } else {
242 preferences_[prefs_list::gui2_theme] = "default";
243 }
244
246 }
247}
248
255
256std::set<std::string> prefs::all_attributes()
257{
258 std::set<std::string> attrs;
259
260 // attributes that exist in the preferences file
261 for(const auto& attr : preferences_.attribute_range()) {
262 attrs.emplace(attr.first);
263 }
264 // all mainline preference attributes, whether they're set or not
265 for(const auto attr : prefs_list::values) {
266 attrs.emplace(attr);
267 }
268
269 return attrs;
270}
271
273{
275 try{
276 config default_prefs;
277 config unsynced_prefs;
278 config synced_prefs;
279#ifdef DEFAULT_PREFS_PATH
280 // NOTE: the system preferences file is only ever relevant for the first time wesnoth starts
281 // any default values will subsequently be written to the normal preferences files, which takes precedence over any values in the system preferences file
282 {
284 default_prefs = io::read(*stream);
285 }
286#endif
287 {
289 unsynced_prefs = io::read(*stream);
290 }
291
292 {
294 synced_prefs = io::read(*stream);
295 }
296
297 preferences_.merge_with(default_prefs);
298 preferences_.merge_with(unsynced_prefs);
299 preferences_.merge_with(synced_prefs);
300
301 // check for any unknown preferences
302 for(const auto& [key, _] : synced_prefs.attribute_range()) {
304 unknown_synced_attributes_.insert(key);
305 }
306 }
307 for(const auto& [key, _] : unsynced_prefs.attribute_range()) {
310 }
311 }
312
313 for(const auto [key, _] : synced_prefs.all_children_view()) {
315 unknown_synced_children_.insert(key);
316 }
317 }
318 for(const auto [key, _] : unsynced_prefs.all_children_view()) {
320 unknown_unsynced_children_.insert(key);
321 }
322 }
323 } catch(const config::error& e) {
324 ERR_CFG << "Error loading preference, message: " << e.what();
325 }
326
329
330 /*
331 completed_campaigns = "A,B,C"
332 [completed_campaigns]
333 [campaign]
334 name = "A"
335 difficulty_levels = "EASY,MEDIUM"
336 [/campaign]
337 [/completed_campaigns]
338 */
339 // presumably for backwards compatibility?
340 // nothing actually sets the attribute, only the child tags
341 for(const std::string& c : utils::split(preferences_[prefs_list::completed_campaigns])) {
342 completed_campaigns_[c]; // create the elements
343 }
344
345 if(auto ccc = get_child(prefs_list::completed_campaigns)) {
346 for(const config& cc : ccc->child_range("campaign")) {
347 std::set<std::string>& d = completed_campaigns_[cc["name"]];
348 std::vector<std::string> nd = utils::split(cc["difficulty_levels"]);
349 std::copy(nd.begin(), nd.end(), std::inserter(d, d.begin()));
350 }
351 }
352
353 encountered_units_set_ = utils::split_set(preferences_[prefs_list::encountered_units].str());
354
355 const t_translation::ter_list terrain(t_translation::read_list(preferences_[prefs_list::encountered_terrain_list].str()));
356 encountered_terrains_set_.insert(terrain.begin(), terrain.end());
357
358 if(auto history = get_child(prefs_list::history)) {
359 /* Structure of the history
360 [history]
361 [history_id]
362 [line]
363 message = foobar
364 [/line]
365 */
366 for(const auto [key, cfg] : history->all_children_view()) {
367 for(const config& l : cfg.child_range("line")) {
368 history_map_[key].push_back(l["message"]);
369 }
370 }
371 }
372}
373
375{
376#ifndef _WIN32
377 bool synced_prefs_file_existed = filesystem::file_exists(filesystem::get_synced_prefs_file());
378 bool unsynced_prefs_file_existed = filesystem::file_exists(filesystem::get_unsynced_prefs_file());
379#endif
380
381 config synced;
382 config unsynced;
383
384 for(const char* attr : synced_attributes_) {
385 if(preferences_.has_attribute(attr)) {
386 synced[attr] = preferences_[attr];
387 }
388 }
389 for(const char* attr : synced_children_) {
390 for(const auto& child : preferences_.child_range(attr)) {
391 synced.add_child(attr, child);
392 }
393 }
394
395 for(const char* attr : unsynced_attributes_) {
396 if(preferences_.has_attribute(attr)) {
397 unsynced[attr] = preferences_[attr];
398 }
399 }
400 for(const char* attr : unsynced_children_) {
401 for(const auto& child : preferences_.child_range(attr)) {
402 unsynced.add_child(attr, child);
403 }
404 }
405
406 // write any unknown preferences back out
407 for(const std::string& attr : unknown_synced_attributes_) {
408 synced[attr] = preferences_[attr];
409 }
410 for(const std::string& attr : unknown_synced_children_) {
411 for(const auto& child : preferences_.child_range(attr)) {
412 synced.add_child(attr, child);
413 }
414 }
415
416 for(const std::string& attr : unknown_unsynced_attributes_) {
417 unsynced[attr] = preferences_[attr];
418 }
419 for(const std::string& attr : unknown_unsynced_children_) {
420 for(const auto& child : preferences_.child_range(attr)) {
421 unsynced.add_child(attr, child);
422 }
423 }
424
425 try {
427 } catch(const filesystem::io_exception&) {
428 ERR_FS << "error writing to synced preferences file '" << filesystem::get_synced_prefs_file() << "'";
429 }
430
431 try {
433 } catch(const filesystem::io_exception&) {
434 ERR_FS << "error writing to unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
435 }
436
438
439#ifndef _WIN32
440 if(!synced_prefs_file_existed) {
441 if(chmod(filesystem::get_synced_prefs_file().c_str(), 0600) == -1) {
442 ERR_FS << "error setting permissions of preferences file '" << filesystem::get_synced_prefs_file() << "'";
443 }
444 }
445 if(!unsynced_prefs_file_existed) {
446 if(chmod(filesystem::get_unsynced_prefs_file().c_str(), 0600) == -1) {
447 ERR_FS << "error setting permissions of unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
448 }
449 }
450#endif
451}
452
454{
455 // Zero them before clearing.
456 // Probably overly paranoid, but doesn't hurt?
457 for(auto& cred : credentials_) {
458 std::fill(cred.username.begin(), cred.username.end(), '\0');
459 std::fill(cred.server.begin(), cred.server.end(), '\0');
460 }
461 credentials_.clear();
462}
463
465{
466 if(!remember_password()) {
467 return;
468 }
470 std::string cred_file = filesystem::get_credentials_file();
471 if(!filesystem::file_exists(cred_file)) {
472 return;
473 }
474 filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
475 // Credentials file is a binary blob, so use streambuf iterator
476 preferences::secure_buffer data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
478 if(data.empty() || data[0] != pref_constants::CREDENTIAL_SEPARATOR) {
479 ERR_CFG << "Invalid data in credentials file";
480 return;
481 }
482 for(const std::string& elem : utils::split(std::string(data.begin(), data.end()), pref_constants::CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
483 std::size_t at = elem.find_last_of('@');
484 std::size_t eq = elem.find_first_of('=', at + 1);
485 if(at != std::string::npos && eq != std::string::npos) {
486 preferences::secure_buffer key(elem.begin() + eq + 1, elem.end());
487 credentials_.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), unescape(key));
488 }
489 }
490}
491
493{
494 if(!remember_password()) {
496 return;
497 }
498
499#ifndef _WIN32
501#endif
502
503 preferences::secure_buffer credentials_data;
504 for(const auto& cred : credentials_) {
505 credentials_data.push_back(pref_constants::CREDENTIAL_SEPARATOR);
506 credentials_data.insert(credentials_data.end(), cred.username.begin(), cred.username.end());
507 credentials_data.push_back('@');
508 credentials_data.insert(credentials_data.end(), cred.server.begin(), cred.server.end());
509 credentials_data.push_back('=');
510 preferences::secure_buffer key_escaped = escape(cred.key);
511 credentials_data.insert(credentials_data.end(), key_escaped.begin(), key_escaped.end());
512 }
513 try {
515 preferences::secure_buffer encrypted = aes_encrypt(credentials_data, build_key("global", get_system_username()));
516 credentials_file->write(reinterpret_cast<const char*>(encrypted.data()), encrypted.size());
517 } catch(const filesystem::io_exception&) {
518 ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'";
519 }
520
521#ifndef _WIN32
522 if(!creds_file_existed) {
523 if(chmod(filesystem::get_credentials_file().c_str(), 0600) == -1) {
524 ERR_FS << "error setting permissions of credentials file '" << filesystem::get_credentials_file() << "'";
525 }
526 }
527#endif
528}
529
530//
531// helpers
532//
533void prefs::set_child(const std::string& key, const config& val) {
535 preferences_.add_child(key, val);
536}
537
539{
540 return preferences_.optional_child(key);
541}
542
543std::string prefs::get(const std::string& key, const std::string& def) {
544 return preferences_[key].empty() ? def : preferences_[key];
545}
546
548{
549 return preferences_[key];
550}
551
552//
553// accessors
554//
555static std::string fix_orb_color_name(const std::string& color) {
556 if (color.substr(0,4) == "orb_") {
557 if(color[4] >= '0' && color[4] <= '9') {
558 return color.substr(5);
559 } else {
560 return color.substr(4);
561 }
562 }
563 return color;
564}
565
566std::string prefs::allied_color() {
567 std::string ally_color = preferences_[prefs_list::ally_orb_color].str();
568 if (ally_color.empty())
570 return fix_orb_color_name(ally_color);
571}
572void prefs::set_allied_color(const std::string& color_id) {
573 preferences_[prefs_list::ally_orb_color] = color_id;
574}
575
576std::string prefs::enemy_color() {
577 std::string enemy_color = preferences_[prefs_list::enemy_orb_color].str();
578 if (enemy_color.empty())
581}
582void prefs::set_enemy_color(const std::string& color_id) {
583 preferences_[prefs_list::enemy_orb_color] = color_id;
584}
585
586std::string prefs::moved_color() {
587 std::string moved_color = preferences_[prefs_list::moved_orb_color].str();
588 if (moved_color.empty())
591}
592void prefs::set_moved_color(const std::string& color_id) {
593 preferences_[prefs_list::moved_orb_color] = color_id;
594}
595
596std::string prefs::unmoved_color() {
597 std::string unmoved_color = preferences_[prefs_list::unmoved_orb_color].str();
598 if (unmoved_color.empty())
601}
602void prefs::set_unmoved_color(const std::string& color_id) {
603 preferences_[prefs_list::unmoved_orb_color] = color_id;
604}
605
606std::string prefs::partial_color() {
607 std::string partmoved_color = preferences_[prefs_list::partial_orb_color].str();
608 if (partmoved_color.empty())
610 return fix_orb_color_name(partmoved_color);
611}
612void prefs::set_partial_color(const std::string& color_id) {
613 preferences_[prefs_list::partial_orb_color] = color_id;
614}
616 std::string reachmap_color = preferences_[prefs_list::reach_map_color].str();
617 if (reachmap_color.empty())
619 return fix_orb_color_name(reachmap_color);
620}
621void prefs::set_reach_map_color(const std::string& color_id) {
622 preferences_[prefs_list::reach_map_color] = color_id;
623}
624
626 std::string reachmap_enemy_color = preferences_[prefs_list::reach_map_enemy_color].str();
627 if (reachmap_enemy_color.empty())
629 return fix_orb_color_name(reachmap_enemy_color);
630}
631void prefs::set_reach_map_enemy_color(const std::string& color_id) {
632 preferences_[prefs_list::reach_map_enemy_color] = color_id;
633}
634
636{
637 return preferences_[prefs_list::reach_map_border_opacity].to_int(game_config::reach_map_border_opacity);
638}
639
640void prefs::set_reach_map_border_opacity(const int new_opacity)
641{
642 preferences_[prefs_list::reach_map_border_opacity] = new_opacity;
643}
644
646{
647 return preferences_[prefs_list::reach_map_tint_opacity].to_int(game_config::reach_map_tint_opacity);
648}
649
650void prefs::set_reach_map_tint_opacity(const int new_opacity)
651{
652 preferences_[prefs_list::reach_map_tint_opacity] = new_opacity;
653}
654
656{
657 return preferences_[prefs_list::reach_map_darken_opacity].to_int(game_config::reach_map_darken_opacity);
658}
659
660void prefs::set_reach_map_darken_opacity(const int new_opacity)
661{
662 preferences_[prefs_list::reach_map_darken_opacity] = new_opacity;
663}
664
666{
667 return preferences_[prefs_list::reach_map_darken_tex].to_bool(game_config::reach_map_darken_tex);
668}
669
671{
672 preferences_[prefs_list::reach_map_darken_tex] = value;
673}
674
676{
677 const unsigned x_res = preferences_[prefs_list::xresolution].to_unsigned();
678 const unsigned y_res = preferences_[prefs_list::yresolution].to_unsigned();
679
680 // Either resolution was unspecified, return default.
681 if(x_res == 0 || y_res == 0) {
683 }
684
685 return point(
686 std::max<unsigned>(x_res, pref_constants::min_window_width),
687 std::max<unsigned>(y_res, pref_constants::min_window_height)
688 );
689}
690
692{
693 preferences_[prefs_list::xresolution] = std::to_string(res.x);
694 preferences_[prefs_list::yresolution] = std::to_string(res.y);
695}
696
698{
699 // For now this has a minimum value of 1 and a maximum of 4.
700 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);
701}
702
703void prefs::set_pixel_scale(const int scale)
704{
705 preferences_[prefs_list::pixel_scale] = std::clamp(scale, pref_constants::min_pixel_scale, pref_constants::max_pixel_scale);
706}
707
709{
710 if(video::headless()) {
711 return true;
712 }
713
714 return preferences_[prefs_list::turbo].to_bool();
715}
716
717void prefs::set_turbo(bool ison)
718{
719 preferences_[prefs_list::turbo] = ison;
720}
721
723{
724 // Clip at 80 because if it's too low it'll cause crashes
725 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);
726}
727
729{
730 preferences_[prefs_list::font_scale] = std::clamp(scale, pref_constants::min_font_scaling, pref_constants::max_font_scaling);
731}
732
734{
735 return (size * font_scaling()) / 100;
736}
737
739{
740 return preferences_[prefs_list::keepalive_timeout].to_int(20);
741}
742
744{
745 preferences_[prefs_list::keepalive_timeout] = std::abs(seconds);
746}
747
749{
750 return sound::volume::from_percent(preferences_[prefs_list::music_volume].to_double(100.f));
751}
752
754{
755 if(music_volume() == vol) {
756 return;
757 }
758
759 preferences_[prefs_list::music_volume] = vol.as_percent();
761}
762
764{
765 return sound::volume::from_percent(preferences_[prefs_list::sound_volume].to_double(100.f));
766}
767
769{
770 if(sound_volume() == vol) {
771 return;
772 }
773
774 preferences_[prefs_list::sound_volume] = vol.as_percent();
776}
777
779{
780 return sound::volume::from_percent(preferences_[prefs_list::bell_volume].to_double(100.f));
781}
782
784{
785 if(bell_volume() == vol) {
786 return;
787 }
788
789 preferences_[prefs_list::bell_volume] = vol.as_percent();
791}
792
793// old pref name had uppercase UI
795{
796 if(preferences_.has_attribute(prefs_list::ui_volume)) {
797 return sound::volume::from_percent(preferences_[prefs_list::ui_volume].to_double(100.f));
798 } else {
799 return sound::volume::from_percent(preferences_["UI_volume"].to_double(100.f));
800 }
801}
802
804{
805 if(ui_volume() == vol) {
806 return;
807 }
808
809 preferences_[prefs_list::ui_volume] = vol.as_percent();
811}
812
814{
815 return preferences_[prefs_list::turn_bell].to_bool(true);
816}
817
818bool prefs::set_turn_bell(bool is_on)
819{
820 // TODO: the source of truth should be the sound system itself, not prefs
821 if(is_on && !turn_bell()) {
822 preferences_[prefs_list::turn_bell] = true;
824 } else if(!is_on && turn_bell()) {
825 preferences_[prefs_list::turn_bell] = false;
827 }
828
829 return is_on;
830}
831
832// old pref name had uppercase UI
834{
835 if(preferences_.has_attribute(prefs_list::ui_sound)) {
836 return preferences_[prefs_list::ui_sound].to_bool(true);
837 } else {
838 return preferences_["UI_sound"].to_bool(true);
839 }
840}
841
842bool prefs::set_ui_sound(bool is_on)
843{
844 // TODO: the source of truth should be the sound system itself, not prefs
845 if(is_on && !ui_sound_on()) {
846 preferences_[prefs_list::ui_sound] = true;
848 } else if(!is_on && ui_sound_on()) {
849 preferences_[prefs_list::ui_sound] = false;
851 }
852
853 return is_on;
854}
855
857{
858 return preferences_[prefs_list::message_bell].to_bool(true);
859}
860
862{
863 return preferences_[prefs_list::sound].to_bool(true);
864}
865
866bool prefs::set_sound(bool is_on)
867{
868 // TODO: the source of truth should be the sound system itself, not prefs
869 if(is_on && !sound()) {
870 preferences_[prefs_list::sound] = true;
872 } else if(!is_on && sound()) {
873 preferences_[prefs_list::sound] = false;
875 }
876
877 return is_on;
878}
879
881{
882 return preferences_[prefs_list::music].to_bool(true);
883}
884
885bool prefs::set_music(bool is_on)
886{
887 // TODO: the source of truth should be the sound system itself, not prefs
888 if(is_on && !music_on()) {
889 preferences_[prefs_list::music] = true;
891 } else if(!is_on && music_on()) {
892 preferences_[prefs_list::music] = false;
894 }
895
896 return is_on;
897}
898
900{
901 return std::clamp<int>(preferences_[prefs_list::scroll].to_int(50), 1, 100);
902}
903
904void prefs::set_scroll_speed(const int new_speed)
905{
906 preferences_[prefs_list::scroll] = new_speed;
907}
908
910{
911 return preferences_[prefs_list::middle_click_scrolls].to_bool(true);
912}
913
915{
916 return preferences_[prefs_list::scroll_threshold].to_int(10);
917}
918
920{
921 return fps_;
922}
923
925{
926 fps_ = value;
927}
928
933
938
940{
942 preferences_.clear_children(prefs_list::hotkey);
943}
944
945void prefs::add_alias(const std::string &alias, const std::string &command)
946{
947 config &alias_list = preferences_.child_or_add("alias");
948 alias_list[alias] = command;
949}
950
951
953{
954 return get_child(prefs_list::alias);
955}
956
958{
959 return preferences_[prefs_list::confirm_load_save_from_different_version].to_bool(true);
960}
961
963{
964 return preferences_[prefs_list::use_twelve_hour_clock_format].to_bool();
965}
966
968{
969 return sort_order::get_enum(preferences_[prefs_list::addon_manager_saved_order_direction].to_int()).value_or(sort_order::type::none);
970}
971
973{
974 preferences_[prefs_list::addon_manager_saved_order_direction] = sort_order::get_string(value);
975}
976
977bool prefs::achievement(const std::string& content_for, const std::string& id)
978{
979 for(config& ach : preferences_.child_range(prefs_list::achievements))
980 {
981 if(ach["content_for"].str() == content_for)
982 {
983 return utils::contains(utils::split(ach["ids"]), id);
984 }
985 }
986 return false;
987}
988
989void prefs::set_achievement(const std::string& content_for, const std::string& id)
990{
991 for(config& ach : preferences_.child_range(prefs_list::achievements))
992 {
993 // if achievements already exist for this content and the achievement has not already been set, add it
994 if(ach["content_for"].str() == content_for)
995 {
996 std::vector<std::string> ids = utils::split(ach["ids"]);
997
998 if(ids.empty())
999 {
1000 ach["ids"] = id;
1001 }
1002 else if(!utils::contains(ids, id))
1003 {
1004 ach["ids"] = ach["ids"].str() + "," + id;
1005 }
1006 ach.remove_children("in_progress", [&id](config cfg){return cfg["id"].str() == id;});
1007 return;
1008 }
1009 }
1010
1011 // else no achievements have been set for this content yet
1012 config ach;
1013 ach["content_for"] = content_for;
1014 ach["ids"] = id;
1015 preferences_.add_child(prefs_list::achievements, ach);
1016}
1017
1018int prefs::progress_achievement(const std::string& content_for, const std::string& id, int limit, int max_progress, int amount)
1019{
1020 if(achievement(content_for, id))
1021 {
1022 return -1;
1023 }
1024
1025 for(config& ach : preferences_.child_range(prefs_list::achievements))
1026 {
1027 // if achievements already exist for this content and the achievement has not already been set, add it
1028 if(ach["content_for"].str() == content_for)
1029 {
1030 // check if this achievement has progressed before - if so then increment it
1031 for(config& in_progress : ach.child_range("in_progress"))
1032 {
1033 if(in_progress["id"].str() == id)
1034 {
1035 // don't let using 'limit' decrease the achievement's current progress
1036 int starting_progress = in_progress["progress_at"].to_int();
1037 if(starting_progress >= limit) {
1038 return starting_progress;
1039 }
1040
1041 in_progress["progress_at"] = std::clamp(starting_progress + amount, 0, std::min(limit, max_progress));
1042 return in_progress["progress_at"].to_int();
1043 }
1044 }
1045
1046 // else this is the first time this achievement is progressing
1047 if(amount != 0)
1048 {
1049 config set_progress;
1050 set_progress["id"] = id;
1051 set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1052
1053 config& child = ach.add_child("in_progress", set_progress);
1054 return child["progress_at"].to_int();
1055 }
1056 return 0;
1057 }
1058 }
1059
1060 // else not only has this achievement not progressed before, this is the first achievement for this achievement group to be added
1061 if(amount != 0)
1062 {
1063 config ach;
1064 config set_progress;
1065
1066 set_progress["id"] = id;
1067 set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1068
1069 ach["content_for"] = content_for;
1070 ach["ids"] = "";
1071
1072 config& child = ach.add_child("in_progress", set_progress);
1073 preferences_.add_child(prefs_list::achievements, ach);
1074 return child["progress_at"].to_int();
1075 }
1076 return 0;
1077}
1078
1079bool prefs::sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1080{
1081 // this achievement is already completed
1082 if(achievement(content_for, id))
1083 {
1084 return true;
1085 }
1086
1087 for(config& ach : preferences_.child_range(prefs_list::achievements))
1088 {
1089 if(ach["content_for"].str() == content_for)
1090 {
1091 // check if the specific sub-achievement has been completed but the overall achievement is not completed
1092 for(const auto& in_progress : ach.child_range("in_progress"))
1093 {
1094 if(in_progress["id"] == id)
1095 {
1096 return utils::contains(utils::split(in_progress["sub_ids"]), sub_id);
1097 }
1098 }
1099 }
1100 }
1101 return false;
1102}
1103
1104void prefs::set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1105{
1106 // this achievement is already completed
1107 if(achievement(content_for, id))
1108 {
1109 return;
1110 }
1111
1112 for(config& ach : preferences_.child_range(prefs_list::achievements))
1113 {
1114 // if achievements already exist for this content and the achievement has not already been set, add it
1115 if(ach["content_for"].str() == content_for)
1116 {
1117 // check if this achievement has had sub-achievements set before
1118 for(config& in_progress : ach.child_range("in_progress"))
1119 {
1120 if(in_progress["id"].str() == id)
1121 {
1122 std::vector<std::string> sub_ids = utils::split(in_progress["sub_ids"]);
1123
1124 if(!utils::contains(sub_ids, sub_id))
1125 {
1126 in_progress["sub_ids"] = in_progress["sub_ids"].str() + "," + sub_id;
1127 }
1128
1129 in_progress["progress_at"] = sub_ids.size()+1;
1130 return;
1131 }
1132 }
1133
1134 // else if this is the first sub-achievement being set
1135 config set_progress;
1136 set_progress["id"] = id;
1137 set_progress["sub_ids"] = sub_id;
1138 set_progress["progress_at"] = 1;
1139 ach.add_child("in_progress", set_progress);
1140 return;
1141 }
1142 }
1143
1144 // 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
1145 config ach;
1146 config set_progress;
1147
1148 set_progress["id"] = id;
1149 set_progress["sub_ids"] = sub_id;
1150 set_progress["progress_at"] = 1;
1151
1152 ach["content_for"] = content_for;
1153 ach["ids"] = "";
1154
1155 ach.add_child("in_progress", set_progress);
1156 preferences_.add_child(prefs_list::achievements, ach);
1157}
1158
1160{
1161 return preferences_[prefs_list::show_deprecation].to_bool(def);
1162}
1163
1165{
1166 return preferences_[prefs_list::scroll_when_mouse_outside].to_bool(def);
1167}
1168
1170{
1171 set_child(prefs_list::dir_bookmarks, cfg);
1172}
1174{
1175 return get_child(prefs_list::dir_bookmarks);
1176}
1177
1179{
1180 return preferences_[prefs_list::lobby_auto_open_whisper_windows].to_bool(true);
1181}
1182
1184{
1185 return std::max(std::size_t(1), preferences_[prefs_list::editor_max_recent_files].to_size_t(10));
1186}
1187
1188//
1189// NOTE: The MRU read/save functions enforce the entry count limit in
1190// order to ensure the list on disk doesn't grow forever. Otherwise,
1191// normally this would be the UI's responsibility instead.
1192//
1193
1194std::vector<std::string> prefs::do_read_editor_mru()
1195{
1196 auto cfg = get_child(prefs_list::editor_recent_files);
1197
1198 std::vector<std::string> mru;
1199 if(!cfg) {
1200 return mru;
1201 }
1202
1203 for(const config& child : cfg->child_range("entry"))
1204 {
1205 const std::string& entry = child["path"].str();
1206 if(!entry.empty()) {
1207 mru.push_back(entry);
1208 }
1209 }
1210
1211 mru.resize(std::min(editor_mru_limit(), mru.size()));
1212
1213 return mru;
1214}
1215
1216void prefs::do_commit_editor_mru(const std::vector<std::string>& mru)
1217{
1218 config cfg;
1219 unsigned n = 0;
1220
1221 for(const std::string& entry : mru)
1222 {
1223 if(entry.empty()) {
1224 continue;
1225 }
1226
1227 config& child = cfg.add_child("entry");
1228 child["path"] = entry;
1229
1230 if(++n >= editor_mru_limit()) {
1231 break;
1232 }
1233 }
1234
1235 set_child(prefs_list::editor_recent_files, cfg);
1236}
1237
1238std::vector<std::string> prefs::recent_files()
1239{
1240 return do_read_editor_mru();
1241}
1242
1243void prefs::add_recent_files_entry(const std::string& path)
1244{
1245 if(path.empty()) {
1246 return;
1247 }
1248
1249 std::vector<std::string> mru = do_read_editor_mru();
1250
1251 // Enforce uniqueness. Normally shouldn't do a thing unless somebody
1252 // has been tampering with the preferences file.
1253 utils::erase(mru, path);
1254
1255 mru.insert(mru.begin(), path);
1256 mru.resize(std::min(editor_mru_limit(), mru.size()));
1257
1259}
1260
1262{
1263 return preferences_[prefs_list::color_cursors].to_bool(true);
1264}
1265
1267{
1268 preferences_[prefs_list::color_cursors] = value;
1269
1270 cursor::set();
1271}
1272
1274{
1275 return preferences_[prefs_list::unit_standing_animations].to_bool(true);
1276}
1277
1279{
1280 preferences_[prefs_list::unit_standing_animations] = value;
1281
1283 d->reset_standing_animations();
1284 }
1285}
1286
1288{
1289 std::vector<theme_info> themes = theme::get_basic_theme_info();
1290
1291 if (themes.empty()) {
1293 _("No known themes. Try changing from within an existing game."));
1294
1295 return false;
1296 }
1297
1298 gui2::dialogs::theme_list dlg(themes);
1299
1300 for (std::size_t k = 0; k < themes.size(); ++k) {
1301 if(themes[k].id == theme()) {
1302 dlg.set_selected_index(static_cast<int>(k));
1303 }
1304 }
1305
1306 dlg.show();
1307 const int action = dlg.selected_index();
1308
1309 if (action >= 0) {
1310 set_theme(themes[action].id);
1311 if(display::get_singleton() && resources::gamedata && resources::gamedata->get_theme().empty()) {
1312 display::get_singleton()->set_theme(themes[action].id);
1313 }
1314
1315 return true;
1316 }
1317
1318 return false;
1319}
1320
1322{
1323 const std::string filename = filesystem::get_wesnothd_name();
1324
1325 const std::string& old_path = filesystem::directory_name(get_mp_server_program_name());
1326 std::string path =
1327 !old_path.empty() && filesystem::is_directory(old_path)
1328 ? old_path : filesystem::get_exe_dir();
1329
1330 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}});
1331
1333
1334 dlg.set_title(_("Find Server Application"))
1335 .set_message(msg)
1336 .set_ok_label(_("Select"))
1337 .set_read_only(true)
1339 .set_path(path);
1340
1341 if(dlg.show()) {
1342 path = dlg.path();
1344 }
1345}
1346
1347std::string prefs::theme()
1348{
1349 if(video::headless()) {
1350 static const std::string null_theme = "null";
1351 return null_theme;
1352 }
1353
1354 std::string res = preferences_[prefs_list::theme];
1355 if(res.empty()) {
1356 return "Default";
1357 }
1358
1359 return res;
1360}
1361
1362void prefs::set_theme(const std::string& theme)
1363{
1364 if(theme != "null") {
1365 preferences_[prefs_list::theme] = theme;
1366 }
1367}
1368
1369void prefs::set_mp_server_program_name(const std::string& path)
1370{
1371 if(path.empty()) {
1372 preferences_.remove_attribute(prefs_list::mp_server_program_name);
1373 } else {
1374 preferences_[prefs_list::mp_server_program_name] = path;
1375 }
1376}
1377
1379{
1380 return preferences_[prefs_list::mp_server_program_name].str();
1381}
1382
1383const std::map<std::string, preferences::acquaintance>& prefs::get_acquaintances()
1384{
1385 return acquaintances_;
1386}
1387
1388const std::string prefs::get_ignored_delim()
1389{
1390 std::vector<std::string> ignored;
1391
1392 for(const auto& person : acquaintances_) {
1393 if(person.second.get_status() == "ignore") {
1394 ignored.push_back(person.second.get_nick());
1395 }
1396 }
1397
1398 return utils::join(ignored);
1399}
1400
1401// returns acquaintances in the form nick => notes where the status = filter
1402std::map<std::string, std::string> prefs::get_acquaintances_nice(const std::string& filter)
1403{
1404 std::map<std::string, std::string> ac_nice;
1405
1406 for(const auto& a : acquaintances_) {
1407 if(a.second.get_status() == filter) {
1408 ac_nice[a.second.get_nick()] = a.second.get_notes();
1409 }
1410 }
1411
1412 return ac_nice;
1413}
1414
1415std::pair<preferences::acquaintance*, bool> prefs::add_acquaintance(const std::string& nick, const std::string& mode, const std::string& notes)
1416{
1417 if(!utils::isvalid_wildcard(nick)) {
1418 return std::pair(nullptr, false);
1419 }
1420
1421 preferences::acquaintance new_entry(nick, mode, notes);
1422 auto [iter, added_new] = acquaintances_.insert_or_assign(nick, new_entry);
1423
1424 return std::pair(&iter->second, added_new);
1425}
1426
1427bool prefs::remove_acquaintance(const std::string& nick)
1428{
1429 std::map<std::string, preferences::acquaintance>::iterator i = acquaintances_.find(nick);
1430
1431 // nick might include the notes, depending on how we're removing
1432 if(i == acquaintances_.end()) {
1433 std::size_t pos = nick.find_first_of(' ');
1434
1435 if(pos != std::string::npos) {
1436 i = acquaintances_.find(nick.substr(0, pos));
1437 }
1438 }
1439
1440 if(i == acquaintances_.end()) {
1441 return false;
1442 }
1443
1444 acquaintances_.erase(i);
1445
1446 return true;
1447}
1448
1449bool prefs::is_friend(const std::string& nick)
1450{
1451 const auto it = acquaintances_.find(nick);
1452
1453 if(it == acquaintances_.end()) {
1454 return false;
1455 } else {
1456 return it->second.get_status() == "friend";
1457 }
1458}
1459
1460bool prefs::is_ignored(const std::string& nick)
1461{
1462 const auto it = acquaintances_.find(nick);
1463
1464 if(it == acquaintances_.end()) {
1465 return false;
1466 } else {
1467 return it->second.get_status() == "ignore";
1468 }
1469}
1470
1471void prefs::add_completed_campaign(const std::string& campaign_id, const std::string& difficulty_level)
1472{
1473 completed_campaigns_[campaign_id].insert(difficulty_level);
1474}
1475
1476bool prefs::is_campaign_completed(const std::string& campaign_id)
1477{
1478 return completed_campaigns_.count(campaign_id) != 0;
1479}
1480
1481bool prefs::is_campaign_completed(const std::string& campaign_id, const std::string& difficulty_level)
1482{
1483 const auto it = completed_campaigns_.find(campaign_id);
1484 return it == completed_campaigns_.end() ? false : it->second.count(difficulty_level) != 0;
1485}
1486
1487bool prefs::parse_should_show_lobby_join(const std::string& sender, const std::string& message)
1488{
1489 // If it's actually not a lobby join or leave message return true (show it).
1490 if(sender != "server") {
1491 return true;
1492 }
1493
1494 std::string::size_type pos = message.find(" has logged into the lobby");
1495 if(pos == std::string::npos) {
1496 pos = message.find(" has disconnected");
1497 if(pos == std::string::npos) {
1498 return true;
1499 }
1500 }
1501
1504 return false;
1505 }
1506
1508 return true;
1509 }
1510
1511 return is_friend(message.substr(0, pos));
1512}
1513
1515{
1516 std::string pref = preferences_[prefs_list::lobby_joins];
1517 if(pref == "friends") {
1519 } else if(pref == "all") {
1521 } else if(pref == "none") {
1523 } else {
1525 }
1526}
1527
1529{
1531 preferences_[prefs_list::lobby_joins] = "friends";
1532 } else if(show == pref_constants::lobby_joins::show_all) {
1533 preferences_[prefs_list::lobby_joins] = "all";
1534 } else if(show == pref_constants::lobby_joins::show_none) {
1535 preferences_[prefs_list::lobby_joins] = "none";
1536 }
1537}
1538
1539const std::vector<game_config::server_info>& prefs::builtin_servers_list()
1540{
1541 static std::vector<game_config::server_info> pref_servers = game_config::server_list;
1542 return pref_servers;
1543}
1544
1546{
1547 config preset{ prefs_list::game_preset, std::move(preset_data) };
1548
1549 int min = 0;
1550 for(const auto& c : preferences_.child_range(prefs_list::game_preset)) {
1551 min = std::min(min, c["id"].to_int());
1552 }
1553 preset.mandatory_child(prefs_list::game_preset)["id"] = min-1;
1554 preferences_.append(preset);
1555}
1556
1558{
1559 preferences_.remove_children(prefs_list::game_preset, [&id](const config& preset) { return preset["id"].to_int() == id; });
1560}
1561
1563{
1564 return preferences_.child_range(prefs_list::game_preset);
1565}
1566
1568{
1569 return preferences_.find_child(prefs_list::game_preset, "id", std::to_string(id));
1570}
1571
1572std::vector<game_config::server_info> prefs::user_servers_list()
1573{
1574 std::vector<game_config::server_info> pref_servers;
1575
1576 for(const config& server : preferences_.child_range(prefs_list::server)) {
1577 pref_servers.emplace_back();
1578 pref_servers.back().name = server["name"].str();
1579 pref_servers.back().address = server["address"].str();
1580 }
1581
1582 return pref_servers;
1583}
1584
1585void prefs::set_user_servers_list(const std::vector<game_config::server_info>& value)
1586{
1587 preferences_.clear_children(prefs_list::server);
1588
1589 for(const auto& svinfo : value) {
1590 config& sv_cfg = preferences_.add_child(prefs_list::server);
1591 sv_cfg["name"] = svinfo.name;
1592 sv_cfg["address"] = svinfo.address;
1593 }
1594}
1595
1597{
1598 std::string res = preferences_[prefs_list::host];
1599 if(res.empty()) {
1600 return builtin_servers_list().front().address;
1601 } else {
1602 return res;
1603 }
1604}
1605
1606void prefs::set_network_host(const std::string& host)
1607{
1608 preferences_[prefs_list::host] = host;
1609}
1610
1612{
1613 if(!preferences_[prefs_list::campaign_server].empty()) {
1614 return preferences_[prefs_list::campaign_server].str();
1615 } else {
1617 }
1618}
1619
1620void prefs::set_campaign_server(const std::string& host)
1621{
1622 preferences_[prefs_list::campaign_server] = host;
1623}
1624
1626{
1627 return preferences_[prefs_list::show_combat].to_bool(true);
1628}
1629
1631{
1633 return option_values_;
1634 }
1635
1636 if(!get_child(prefs_list::options)) {
1637 // It may be an invalid config, which would cause problems in
1638 // multiplayer_create, so let's replace it with an empty but valid
1639 // config
1641 } else {
1642 option_values_ = *get_child(prefs_list::options);
1643 }
1644
1645 options_initialized_ = true;
1646
1647 return option_values_;
1648}
1649
1650void prefs::set_options(const config& values)
1651{
1652 set_child(prefs_list::options, values);
1653 options_initialized_ = false;
1654}
1655
1656std::chrono::seconds prefs::countdown_init_time()
1657{
1658 return chrono::parse_duration(preferences_[prefs_list::mp_countdown_init_time], 240s);
1659}
1660
1661void prefs::set_countdown_init_time(const std::chrono::seconds& value)
1662{
1663 preferences_[prefs_list::mp_countdown_init_time] = std::clamp(value, 0s, 1500s);
1664}
1665
1667{
1668 preferences_.remove_attribute(prefs_list::mp_countdown_init_time);
1669}
1670
1672{
1673 return chrono::parse_duration(preferences_[prefs_list::mp_countdown_reservoir_time], 360s);
1674}
1675
1676void prefs::set_countdown_reservoir_time(const std::chrono::seconds& value)
1677{
1678 preferences_[prefs_list::mp_countdown_reservoir_time] = std::clamp(value, 30s, 1500s);
1679}
1680
1682{
1683 preferences_.remove_attribute(prefs_list::mp_countdown_reservoir_time);
1684}
1685
1686std::chrono::seconds prefs::countdown_turn_bonus()
1687{
1688 return chrono::parse_duration(preferences_[prefs_list::mp_countdown_turn_bonus], 240s);
1689}
1690
1691void prefs::set_countdown_turn_bonus(const std::chrono::seconds& value)
1692{
1693 preferences_[prefs_list::mp_countdown_turn_bonus] = std::clamp(value, 0s, 300s);
1694}
1695
1697{
1698 preferences_.remove_attribute(prefs_list::mp_countdown_turn_bonus);
1699}
1700
1701std::chrono::seconds prefs::countdown_action_bonus()
1702{
1703 return chrono::parse_duration(preferences_[prefs_list::mp_countdown_action_bonus], 0s);
1704}
1705
1706void prefs::set_countdown_action_bonus(const std::chrono::seconds& value)
1707{
1708 preferences_[prefs_list::mp_countdown_action_bonus] = std::clamp(value, 0s, 30s);
1709}
1710
1712{
1713 preferences_.remove_attribute(prefs_list::mp_countdown_action_bonus);
1714}
1715
1716std::chrono::minutes prefs::chat_message_aging()
1717{
1718 return chrono::parse_duration(preferences_[prefs_list::chat_message_aging], 20min);
1719}
1720
1721void prefs::set_chat_message_aging(const std::chrono::minutes& value)
1722{
1723 preferences_[prefs_list::chat_message_aging] = value;
1724}
1725
1727{
1728 return settings::get_village_gold(preferences_[prefs_list::mp_village_gold]);
1729}
1730
1732{
1733 preferences_[prefs_list::mp_village_gold] = value;
1734}
1735
1737{
1738 return settings::get_village_support(preferences_[prefs_list::mp_village_support]);
1739}
1740
1742{
1743 preferences_[prefs_list::mp_village_support] = std::to_string(value);
1744}
1745
1747{
1748 return settings::get_xp_modifier(preferences_[prefs_list::mp_xp_modifier]);
1749}
1750
1752{
1753 preferences_[prefs_list::mp_xp_modifier] = value;
1754}
1755
1756const std::vector<std::string>& prefs::modifications(bool mp)
1757{
1759 if(mp) {
1760 mp_modifications_ = utils::split(preferences_[prefs_list::mp_modifications].str(), ',');
1762 } else {
1763 sp_modifications_ = utils::split(preferences_[prefs_list::sp_modifications].str(), ',');
1765 }
1766 }
1767
1769}
1770
1771void prefs::set_modifications(const std::vector<std::string>& value, bool mp)
1772{
1773 if(mp) {
1774 preferences_[prefs_list::mp_modifications] = utils::join(value, ",");
1776 } else {
1777 preferences_[prefs_list::sp_modifications] = utils::join(value, ",");
1779 }
1780}
1781
1783{
1784 return message_private_on_;
1785}
1786
1791
1793{
1794 const std::string& choice = preferences_[prefs_list::compress_saves];
1795
1796 // "yes" was used in 1.11.7 and earlier; the compress_saves
1797 // option used to be a toggle for gzip in those versions.
1798 if(choice.empty() || choice == "gzip" || choice == "yes") {
1800 } else if(choice == "bzip2") {
1802 } else if(choice == "none" || choice == "no") { // see above
1804 } /*else*/
1805
1806 // In case the preferences file was created by a later version
1807 // supporting some algorithm we don't; although why would anyone
1808 // playing a game need more algorithms, really...
1810}
1811
1812std::string prefs::get_chat_timestamp(const std::chrono::system_clock::time_point& t)
1813{
1814 if(chat_timestamp()) {
1815 if(use_twelve_hour_clock_format() == false) {
1816 return chrono::format_local_timestamp(t, _("[%H:%M]")) + " ";
1817 } else {
1818 return chrono::format_local_timestamp(t, _("[%I:%M %p]")) + " ";
1819 }
1820 }
1821
1822 return "";
1823}
1824
1825std::set<std::string>& prefs::encountered_units()
1826{
1828}
1829
1830std::set<t_translation::terrain_code>& prefs::encountered_terrains()
1831{
1833}
1834
1835/**
1836 * Returns a pointer to the history vector associated with given id
1837 * making a new one if it doesn't exist.
1838 *
1839 * @todo FIXME only used for gui2. Could be used for the above histories.
1840 */
1841std::vector<std::string>* prefs::get_history(const std::string& id)
1842{
1843 return &history_map_[id];
1844}
1845
1847{
1848 const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1849 return confirmation == "green" || confirmation == "yes";
1850}
1851
1853{
1854 return preferences_[prefs_list::confirm_end_turn] == "yellow";
1855}
1856
1858{
1859 // This is very non-intrusive so it is on by default
1860 const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1861 return confirmation == "no_moves" || confirmation.empty();
1862}
1863
1864void prefs::encounter_recruitable_units(const std::vector<team>& teams)
1865{
1866 for(const team& help_team : teams) {
1867 help_team.log_recruitable();
1868 encountered_units_set_.insert(help_team.recruits().begin(), help_team.recruits().end());
1869 }
1870}
1871
1873{
1874 for(const auto& help_unit : units) {
1875 encountered_units_set_.insert(help_unit.type_id());
1876 }
1877}
1878
1879void prefs::encounter_recallable_units(const std::vector<team>& teams)
1880{
1881 for(const team& t : teams) {
1882 for(const unit_const_ptr u : t.recall_list()) {
1883 encountered_units_set_.insert(u->type_id());
1884 }
1885 }
1886}
1887
1889{
1890 map.for_each_loc([&](const map_location& loc) {
1892 });
1893}
1894
1896{
1897 encountered_terrains().insert(terrain.number());
1898 for(const t_translation::terrain_code& t : terrain.union_type()) {
1899 encountered_terrains().insert(t);
1900 }
1901}
1902
1904{
1905 encounter_recruitable_units(gameboard_.teams());
1906 encounter_start_units(gameboard_.units());
1907 encounter_recallable_units(gameboard_.teams());
1908 encounter_map_terrain(gameboard_.map());
1909}
1910
1912{
1913 preferences_.remove_attribute(prefs_list::player_joins_sound);
1914 preferences_.remove_attribute(prefs_list::player_joins_notif);
1915 preferences_.remove_attribute(prefs_list::player_joins_lobby);
1916 preferences_.remove_attribute(prefs_list::player_leaves_sound);
1917 preferences_.remove_attribute(prefs_list::player_leaves_notif);
1918 preferences_.remove_attribute(prefs_list::player_leaves_lobby);
1919 preferences_.remove_attribute(prefs_list::private_message_sound);
1920 preferences_.remove_attribute(prefs_list::private_message_notif);
1921 preferences_.remove_attribute(prefs_list::private_message_lobby);
1922 preferences_.remove_attribute(prefs_list::friend_message_sound);
1923 preferences_.remove_attribute(prefs_list::friend_message_notif);
1924 preferences_.remove_attribute(prefs_list::friend_message_lobby);
1925 preferences_.remove_attribute(prefs_list::public_message_sound);
1926 preferences_.remove_attribute(prefs_list::public_message_notif);
1927 preferences_.remove_attribute(prefs_list::public_message_lobby);
1928 preferences_.remove_attribute(prefs_list::server_message_sound);
1929 preferences_.remove_attribute(prefs_list::server_message_notif);
1930 preferences_.remove_attribute(prefs_list::server_message_lobby);
1931 preferences_.remove_attribute(prefs_list::ready_for_start_sound);
1932 preferences_.remove_attribute(prefs_list::ready_for_start_notif);
1933 preferences_.remove_attribute(prefs_list::ready_for_start_lobby);
1934 preferences_.remove_attribute(prefs_list::game_has_begun_sound);
1935 preferences_.remove_attribute(prefs_list::game_has_begun_notif);
1936 preferences_.remove_attribute(prefs_list::game_has_begun_lobby);
1937 preferences_.remove_attribute(prefs_list::turn_changed_sound);
1938 preferences_.remove_attribute(prefs_list::turn_changed_notif);
1939 preferences_.remove_attribute(prefs_list::turn_changed_lobby);
1940 preferences_.remove_attribute(prefs_list::game_created_sound);
1941 preferences_.remove_attribute(prefs_list::game_created_notif);
1942 preferences_.remove_attribute(prefs_list::game_created_lobby);
1943}
1944
1946{
1947#ifdef _WIN32
1948 wchar_t buffer[300];
1949 DWORD size = 300;
1950 if(GetUserNameW(buffer, &size)) {
1951 //size includes a terminating null character.
1952 assert(size > 0);
1953 return unicode_cast<std::string>(std::wstring_view{buffer});
1954 }
1955#else
1956 if(char* const login = getenv("USER")) {
1957 return login;
1958 }
1959#endif
1960 return {};
1961}
1962
1963preferences::secure_buffer prefs::build_key(const std::string& server, const std::string& login)
1964{
1965 std::string sysname = get_system_username();
1966 preferences::secure_buffer result(std::max<std::size_t>(server.size() + login.size() + sysname.size(), 32));
1967 unsigned char i = 0;
1968 std::generate(result.begin(), result.end(), [&i]() {return 'x' ^ i++;});
1969 std::copy(login.begin(), login.end(), result.begin());
1970 std::copy(sysname.begin(), sysname.end(), result.begin() + login.size());
1971 std::copy(server.begin(), server.end(), result.begin() + login.size() + sysname.size());
1972 return result;
1973}
1974
1976{
1977#ifndef __APPLE__
1978 int update_length;
1979 int extra_length;
1980 int total_length;
1981 // AES IV is generally 128 bits
1982 const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1983 unsigned char encrypted_buffer[1024];
1984
1985 if(plaintext.size() > 1008)
1986 {
1987 ERR_CFG << "Cannot encrypt data larger than 1008 bytes.";
1989 }
1990 DBG_CFG << "Encrypting data with length: " << plaintext.size();
1991
1992 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1993 if(!ctx)
1994 {
1995 ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
1996 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
1998 }
1999
2000 // TODO: use EVP_EncryptInit_ex2 once openssl 3.0 is more widespread
2001 if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv) != 1)
2002 {
2003 ERR_CFG << "AES EVP_EncryptInit_ex failed with error:";
2004 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2005 EVP_CIPHER_CTX_free(ctx);
2007 }
2008
2009 if(EVP_EncryptUpdate(ctx, encrypted_buffer, &update_length, plaintext.data(), plaintext.size()) != 1)
2010 {
2011 ERR_CFG << "AES EVP_EncryptUpdate failed with error:";
2012 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2013 EVP_CIPHER_CTX_free(ctx);
2015 }
2016 DBG_CFG << "Update length: " << update_length;
2017
2018 if(EVP_EncryptFinal_ex(ctx, encrypted_buffer + update_length, &extra_length) != 1)
2019 {
2020 ERR_CFG << "AES EVP_EncryptFinal failed with error:";
2021 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2022 EVP_CIPHER_CTX_free(ctx);
2024 }
2025 DBG_CFG << "Extra length: " << extra_length;
2026
2027 EVP_CIPHER_CTX_free(ctx);
2028
2029 total_length = update_length+extra_length;
2031 for(int i = 0; i < total_length; i++)
2032 {
2033 result.push_back(encrypted_buffer[i]);
2034 }
2035
2036 DBG_CFG << "Successfully encrypted plaintext value of '" << utils::join(plaintext, "") << "' having length " << plaintext.size();
2037 DBG_CFG << "For a total encrypted length of: " << total_length;
2038
2039 return result;
2040#else
2041 std::size_t outWritten = 0;
2042 preferences::secure_buffer result(plaintext.size(), '\0');
2043
2044 CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
2045 kCCAlgorithmRC4,
2046 kCCOptionPKCS7Padding,
2047 key.data(),
2048 key.size(),
2049 nullptr,
2050 plaintext.data(),
2051 plaintext.size(),
2052 result.data(),
2053 result.size(),
2054 &outWritten);
2055
2056 assert(ccStatus == kCCSuccess);
2057 assert(outWritten == plaintext.size());
2058
2059 return result;
2060#endif
2061}
2062
2064{
2065#ifndef __APPLE__
2066 int update_length;
2067 int extra_length;
2068 int total_length;
2069 // AES IV is generally 128 bits
2070 const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
2071 unsigned char plaintext_buffer[1024];
2072
2073 if(encrypted.size() > 1024)
2074 {
2075 ERR_CFG << "Cannot decrypt data larger than 1024 bytes.";
2077 }
2078 DBG_CFG << "Decrypting data with length: " << encrypted.size();
2079
2080 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
2081 if(!ctx)
2082 {
2083 ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
2084 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2086 }
2087
2088 // TODO: use EVP_DecryptInit_ex2 once openssl 3.0 is more widespread
2089 if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.data(), iv) != 1)
2090 {
2091 ERR_CFG << "AES EVP_DecryptInit_ex failed with error:";
2092 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2093 EVP_CIPHER_CTX_free(ctx);
2095 }
2096
2097 if(EVP_DecryptUpdate(ctx, plaintext_buffer, &update_length, encrypted.data(), encrypted.size()) != 1)
2098 {
2099 ERR_CFG << "AES EVP_DecryptUpdate failed with error:";
2100 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2101 EVP_CIPHER_CTX_free(ctx);
2103 }
2104 DBG_CFG << "Update length: " << update_length;
2105
2106 if(EVP_DecryptFinal_ex(ctx, plaintext_buffer + update_length, &extra_length) != 1)
2107 {
2108 ERR_CFG << "AES EVP_DecryptFinal failed with error:";
2109 ERR_CFG << ERR_error_string(ERR_get_error(), nullptr);
2110 EVP_CIPHER_CTX_free(ctx);
2112 }
2113 DBG_CFG << "Extra length: " << extra_length;
2114
2115 EVP_CIPHER_CTX_free(ctx);
2116
2117 total_length = update_length+extra_length;
2119 for(int i = 0; i < total_length; i++)
2120 {
2121 result.push_back(plaintext_buffer[i]);
2122 }
2123
2124 DBG_CFG << "Successfully decrypted data to the value: " << utils::join(result, "");
2125 DBG_CFG << "For a total decrypted length of: " << total_length;
2126
2127 return result;
2128#else
2129 std::size_t outWritten = 0;
2130 preferences::secure_buffer result(encrypted.size(), '\0');
2131
2132 CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
2133 kCCAlgorithmRC4,
2134 kCCOptionPKCS7Padding,
2135 key.data(),
2136 key.size(),
2137 nullptr,
2138 encrypted.data(),
2139 encrypted.size(),
2140 result.data(),
2141 result.size(),
2142 &outWritten);
2143
2144 assert(ccStatus == kCCSuccess);
2145 assert(outWritten == encrypted.size());
2146
2147 // the decrypted result is likely shorter than the encrypted data, so the extra padding needs to be removed.
2148 while(!result.empty() && result.back() == 0) {
2149 result.pop_back();
2150 }
2151
2152 return result;
2153#endif
2154}
2155
2157{
2159 unescaped.reserve(text.size());
2160 bool escaping = false;
2161 for(char c : text) {
2162 if(escaping) {
2163 if(c == '\xa') {
2164 unescaped.push_back('\xc');
2165 } else if(c == '.') {
2166 unescaped.push_back('@');
2167 } else {
2168 unescaped.push_back(c);
2169 }
2170 escaping = false;
2171 } else if(c == '\x1') {
2172 escaping = true;
2173 } else {
2174 unescaped.push_back(c);
2175 }
2176 }
2177 assert(!escaping);
2178 return unescaped;
2179}
2180
2182{
2184 escaped.reserve(text.size());
2185 for(char c : text) {
2186 if(c == '\x1') {
2187 escaped.push_back('\x1');
2188 escaped.push_back('\x1');
2189 } else if(c == '\xc') {
2190 escaped.push_back('\x1');
2191 escaped.push_back('\xa');
2192 } else if(c == '@') {
2193 escaped.push_back('\x1');
2194 escaped.push_back('.');
2195 } else {
2196 escaped.push_back(c);
2197 }
2198 }
2199 return escaped;
2200}
2201
2203{
2204 return preferences_[prefs_list::remember_password].to_bool();
2205}
2206
2208{
2209 preferences_[prefs_list::remember_password] = remember;
2210
2211 if(remember) {
2213 } else {
2215 }
2216}
2217
2218std::string prefs::login()
2219{
2220 std::string name = get("login", pref_constants::EMPTY_LOGIN);
2221 if(name == pref_constants::EMPTY_LOGIN) {
2222 name = get_system_username();
2223 } else if(name.size() > 2 && name.front() == '@' && name.back() == '@') {
2224 name = name.substr(1, name.size() - 2);
2225 } else {
2226 ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)";
2227 }
2228 if(name.empty()) {
2229 return "player";
2230 }
2231 return name;
2232}
2233
2234void prefs::set_login(const std::string& login)
2235{
2236 auto login_clean = login;
2237 boost::trim(login_clean);
2238
2239 preferences_[prefs_list::login] = '@' + login_clean + '@';
2240}
2241
2242std::string prefs::password(const std::string& server, const std::string& login)
2243{
2244 DBG_CFG << "Retrieving password for server: '" << server << "', login: '" << login << "'";
2245 auto login_clean = login;
2246 boost::trim(login_clean);
2247
2248 if(!remember_password()) {
2249 if(!credentials_.empty() && credentials_[0].username == login_clean && credentials_[0].server == server) {
2250 auto temp = aes_decrypt(credentials_[0].key, build_key(server, login_clean));
2251 return std::string(temp.begin(), temp.end());
2252 } else {
2253 return "";
2254 }
2255 }
2256 auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2257 return cred.server == server && cred.username == login_clean;
2258 });
2259 if(cred == credentials_.end()) {
2260 return "";
2261 }
2262 auto temp = aes_decrypt(cred->key, build_key(server, login_clean));
2263 return std::string(temp.begin(), temp.end());
2264}
2265
2266void prefs::set_password(const std::string& server, const std::string& login, const std::string& key)
2267{
2268 DBG_CFG << "Setting password for server: '" << server << "', login: '" << login << "'";
2269 auto login_clean = login;
2270 boost::trim(login_clean);
2271
2272 preferences::secure_buffer temp(key.begin(), key.end());
2273 if(!remember_password()) {
2275 credentials_.emplace_back(login_clean, server, aes_encrypt(temp, build_key(server, login_clean)));
2276 return;
2277 }
2278 auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2279 return cred.server == server && cred.username == login_clean;
2280 });
2281 if(cred == credentials_.end()) {
2282 // This is equivalent to emplace_back, but also returns the iterator to the new element
2283 cred = credentials_.emplace(credentials_.end(), login_clean, server);
2284 }
2285 cred->key = aes_encrypt(temp, build_key(server, login_clean));
2286}
map_location loc
Definition move.cpp:172
#define ERR_CFG
Definition manager.cpp:30
#define ERR_FS
Definition manager.cpp:35
double t
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
void remove_attribute(std::string_view key)
Definition config.cpp:162
config & add_child(std::string_view key)
Definition config.cpp:436
void append(const config &cfg)
Append data from another config object to this one.
Definition config.cpp:188
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
const_attr_itors attribute_range() const
Definition config.cpp:740
config & child_or_add(std::string_view key)
Returns a reference to the first child with the given key.
Definition config.cpp:401
auto all_children_view() const
In-order iteration over all children.
Definition config.hpp:795
boost::iterator_range< child_iterator > child_itors
Definition config.hpp:280
child_itors child_range(std::string_view key)
Definition config.cpp:268
void clear_children(T... keys)
Definition config.hpp:601
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition config.cpp:1097
optional_config_impl< config > find_child(std::string_view 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:764
bool has_attribute(std::string_view key) const
Definition config.cpp:157
bool empty() const
Definition config.cpp:823
void clear()
Definition config.cpp:802
void remove_children(std::string_view key, const std::function< bool(const config &)> &p={})
Removes all children with tag key for which p returns true.
Definition config.cpp:634
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition display.hpp:88
void set_theme(const std::string &new_theme)
Definition display.cpp:246
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
Game board class.
virtual const gamemap & map() const override
virtual const std::vector< team > & teams() const override
virtual const unit_map & units() const override
A class grating read only view to a vector of config objects, viewed as one config with all children ...
config_array_view child_range(std::string_view key) const
static game_config_view wrap(const config &cfg)
void for_each_loc(const F &f) const
Definition map.hpp:140
Encapsulates the map of the game.
Definition map.hpp:176
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition map.cpp:78
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
file_dialog & set_filename(const std::string &value)
Sets the initial file name input but not the path.
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
file_dialog & set_message(const std::string &value)
Sets the current dialog instructions/message text.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
bool show(const unsigned auto_close_time=0)
Shows the window.
int selected_index() const
Returns the selected item index after displaying.
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
const std::string & get_nick() const
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_reach_map_enemy_color(const std::string &color_id)
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_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)
static prefs & get()
std::string get_system_username()
sound::volume music_volume()
void set_sound_volume(sound::volume vol)
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)
void remove_game_preset(int id)
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_
sound::volume sound_volume()
int reach_map_darken_opacity()
bool set_ui_sound(bool ison)
void set_reach_map_border_opacity(const int new_opacity)
std::chrono::minutes chat_message_aging()
void set_login(const std::string &login)
void set_initial_gui2_theme()
const std::string get_ignored_delim()
std::map< std::string, preferences::acquaintance > acquaintances_
static constexpr std::array unsynced_children_
sound::volume bell_volume()
bool fps_
config::child_itors get_game_presets()
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 reach_map_darken_tex()
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 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_
void set_reach_map_tint_opacity(const int new_opacity)
sound::volume ui_volume()
void set_countdown_turn_bonus(const std::chrono::seconds &value)
static constexpr std::array synced_children_
void set_options(const config &values)
int reach_map_tint_opacity()
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 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)
optional_const_config get_game_preset(int id)
void encounter_map_terrain(const gamemap &map)
void set_campaign_rng_mode_default_for_migration()
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)
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)
void set_music_volume(sound::volume vol)
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 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_reach_map_darken_opacity(const int new_opacity)
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()
std::string reach_map_color()
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_
std::string get_mp_server_program_name()
std::string reach_map_enemy_color()
void add_game_preset(config &&preset_data)
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 &)
void set_bell_volume(sound::volume vol)
bool remember_password()
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()
bool use_twelve_hour_clock_format()
static bool no_preferences_save
int reach_map_border_opacity()
void set_reach_map_darken_tex(const bool value)
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 set_reach_map_color(const std::string &color_id)
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()
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()
void set_ui_volume(sound::volume vol)
constexpr float as_percent() const
Definition sound.hpp:124
static constexpr volume from_percent(float percentage)
Definition sound.hpp:121
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
const t_translation::ter_list & union_type() const
Definition terrain.hpp:87
t_translation::terrain_code number() const
Definition terrain.hpp:66
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:1001
Container associating units to locations.
Definition map.hpp:98
const config * cfg
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
static std::string _(const char *str)
Definition gettext.hpp:100
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
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:82
auto format_local_timestamp(const std::chrono::system_clock::time_point &time, std::string_view format="%F %T")
Definition chrono.hpp:72
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition cursor.cpp:151
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
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)
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=std::ios_base::binary, bool create_directory=true)
std::unique_ptr< std::istream > scoped_istream
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
std::string get_wesnothd_name()
std::chrono::system_clock::time_point file_modified_time(const std::string &fname)
Get the modification time of a file.
std::string get_default_prefs_file()
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
std::string partial_orb_color
std::string reach_map_enemy_color
std::string moved_orb_color
std::string unmoved_orb_color
std::string ally_orb_color
std::string enemy_orb_color
std::string reach_map_color
const version_info wesnoth_version(VERSION)
int reach_map_border_opacity
int reach_map_darken_opacity
std::vector< server_info > server_list
bool reach_map_darken_tex
int reach_map_tint_opacity
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.
config read(std::istream &in, abstract_validator *validator)
Definition parser.cpp:610
void write(std::ostream &out, const configr_of &cfg, unsigned int level, bool strong_quotes)
Definition parser.cpp:748
Main entry points of multiplayer mode.
const int min_window_height
const int max_pixel_scale
const std::string EMPTY_LOGIN
const int min_pixel_scale
const std::string default_addons_server
const int def_window_width
const int min_font_scaling
const int min_window_width
const int max_font_scaling
const int def_window_height
const unsigned char CREDENTIAL_SEPARATOR
game_data * gamedata
Definition resources.cpp:22
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_UI_volume(volume vol)
Definition sound.cpp:1062
void set_music_volume(volume vol)
Definition sound.cpp:1022
void stop_music()
Definition sound.cpp:533
void restart_UI_sound()
Definition sound.cpp:589
void set_sound_volume(volume vol)
Definition sound.cpp:1039
void restart_bell()
Definition sound.cpp:581
void stop_UI_sound()
Definition sound.cpp:559
void set_bell_volume(volume vol)
Definition sound.cpp:1052
void restart_sound()
Definition sound.cpp:573
void stop_bell()
Definition sound.cpp:551
void restart_music()
Definition sound.cpp:566
void stop_sound()
Definition sound.cpp:540
std::vector< terrain_code > ter_list
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.
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:119
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
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 Range &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)
bool headless()
The game is running headless.
Definition video.cpp:146
point current_resolution()
The current window size in desktop coordinates.
Definition video.cpp:819
std::string_view data
Definition picture.cpp:188
static lg::log_domain log_filesystem("filesystem")
#define DBG_CFG
#define ERR_ADV
static lg::log_domain advanced_preferences("advanced_preferences")
static std::string fix_orb_color_name(const std::string &color)
static lg::log_domain log_config("config")
std::shared_ptr< const unit > unit_const_ptr
Definition ptr.hpp:27
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
std::string filename
Filename.
Represents a single achievement and its data.
An exception object used when an IO error occurs.
Encapsulates the map of the game.
Definition location.hpp:46
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...
static map_location::direction n
static map_location::direction s
#define i
#define d
#define e
#define c