Add a prompt to migrate data from previous versions of wesnoth.

Currently migrated, if existing:
* Add-ons (redownloaded if present on new add-ons server)
* Preferences file
* Credentials file
This commit is contained in:
Pentarctagon 2021-07-03 13:34:49 -05:00 committed by Pentarctagon
parent 65a951111c
commit c6052e805f
17 changed files with 440 additions and 81 deletions

View file

@ -0,0 +1,137 @@
#textdomain wesnoth-lib
###
### Definition of the window to ask which Wesnoth version to migrate data from
###
[window]
id = "migrate_version_selection"
description = "Dialog to select a previous version of Wesnoth to migrate data from."
[resolution]
definition = "default"
[linked_group]
id = "version_group"
fixed_width = true
[/linked_group]
[linked_group]
id = "radio_options"
fixed_width = true
[/linked_group]
[tooltip]
id = "tooltip"
[/tooltip]
[helptip]
id = "tooltip"
[/helptip]
[grid]
[row]
[column]
horizontal_alignment = "left"
border = "all"
border_size = 5
[label]
definition = "title"
label = _ "Data Migration"
[/label]
[/column]
[/row]
[row]
[column]
horizontal_alignment = "left"
border = "all"
border_size = 5
[label]
definition = "default"
label = _ "Choose which version of Wesnoth to import your data from.
Some settings will not take effect until Wesnoth is restarted."
[/label]
[/column]
[/row]
[row]
[column]
horizontal_grow = true
border = "all"
border_size = 5
[listbox]
id = "versions_listbox"
definition = "default"
horizontal_scrollbar_mode = "never"
[list_definition]
[row]
[column]
vertical_grow = true
horizontal_grow = true
[toggle_panel]
definition = "default"
[grid]
[row]
[column]
grow_factor = 1
horizontal_alignment = "left"
border = "all"
border_size = 5
[label]
id = "version_label"
definition = "default"
[/label]
[/column]
[/row]
[/grid]
[/toggle_panel]
[/column]
[/row]
[/list_definition]
[/listbox]
[/column]
[/row]
[row]
[column]
horizontal_alignment = "right"
[grid]
[row]
[column]
grow_factor = 1
border = "all"
border_size = 5
[button]
id = "ok"
definition = "default"
label = _ "OK"
[/button]
[/column]
[column]
grow_factor = 1
border = "all"
border_size = 5
[button]
id = "cancel"
definition = "default"
label = _ "Skip"
[/button]
[/column]
[/row]
[/grid]
[/column]
[/row]
[/grid]
[/resolution]
[/window]

View file

@ -1288,6 +1288,7 @@
EC84246D18F30D9100FC1EEF /* synced_commands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC84246218F30D9100FC1EEF /* synced_commands.cpp */; };
EC84246E18F30D9100FC1EEF /* synced_context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC84246418F30D9100FC1EEF /* synced_context.cpp */; };
EC84247118F30DB700FC1EEF /* exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC84246F18F30DB700FC1EEF /* exception.cpp */; };
EC86E1F42691568200209B49 /* migrate_version_selection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC86E1F32691568200209B49 /* migrate_version_selection.cpp */; };
ECA1E0F21A1271AC00426E00 /* lua_gui2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECA1E0F11A1271AC00426E00 /* lua_gui2.cpp */; };
ECA1E1011A12755B00426E00 /* mapgen_lua_kernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECA1E1001A12755B00426E00 /* mapgen_lua_kernel.cpp */; };
ECA4A67B1A1EC319006BCCF2 /* lua_fileops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECA4A6781A1EC319006BCCF2 /* lua_fileops.cpp */; };
@ -2689,6 +2690,7 @@
EC84246518F30D9100FC1EEF /* synced_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = synced_context.hpp; sourceTree = "<group>"; };
EC84246F18F30DB700FC1EEF /* exception.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception.cpp; sourceTree = "<group>"; };
EC84247018F30DB700FC1EEF /* exception.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = exception.hpp; sourceTree = "<group>"; };
EC86E1F32691568200209B49 /* migrate_version_selection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = migrate_version_selection.cpp; sourceTree = "<group>"; };
EC89A1061879D17D00A3B0B1 /* lapi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lapi.cpp; sourceTree = "<group>"; };
EC89A1071879D17D00A3B0B1 /* lauxlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lauxlib.cpp; sourceTree = "<group>"; };
EC89A1081879D17D00A3B0B1 /* lbaselib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lbaselib.cpp; sourceTree = "<group>"; };
@ -3587,6 +3589,7 @@
46F92CB02174F6A300602C1C /* lua_interpreter.hpp */,
46F92C662174F6A300602C1C /* message.cpp */,
46F92CF42174F6A300602C1C /* message.hpp */,
EC86E1F32691568200209B49 /* migrate_version_selection.cpp */,
46F92C752174F6A300602C1C /* modal_dialog.cpp */,
46F92C902174F6A300602C1C /* modal_dialog.hpp */,
46F92C722174F6A300602C1C /* modeless_dialog.cpp */,
@ -5138,6 +5141,7 @@
46F92E252174F6A400602C1C /* faction_select.cpp in Sources */,
F4D2DECB14DCA1D000CAFF31 /* client.cpp in Sources */,
EC49A38219F34F1200E78528 /* clipboard.cpp in Sources */,
EC86E1F42691568200209B49 /* migrate_version_selection.cpp in Sources */,
EC179B891E91475300B4178C /* variant_value.cpp in Sources */,
B5599B7E0EC62181008DD061 /* color_range.cpp in Sources */,
ECFA82E3184E59F3006782FB /* command_executor.cpp in Sources */,

View file

@ -206,6 +206,7 @@ gui/dialogs/loading_screen.cpp
gui/dialogs/log_settings.cpp
gui/dialogs/lua_interpreter.cpp
gui/dialogs/message.cpp
gui/dialogs/migrate_version_selection.cpp
gui/dialogs/modal_dialog.cpp
gui/dialogs/modeless_dialog.cpp
gui/dialogs/multiplayer/faction_select.cpp

View file

@ -426,6 +426,11 @@ addons_client::install_result addons_client::do_resolve_addon_dependencies(const
std::vector<std::string> missing_deps;
std::vector<std::string> broken_deps;
// if two add-ons both have the same dependency and are being downloaded in a batch (such as via the adhoc connection)
// then the version cache will not be updated after the first is downloaded
// which will result in it being treated as version 0.0.0, which is then interpreted as being "upgradeable"
// which then causes the user to be prompted to download the same dependency multiple times
version_info unknown_version(0, 0, 0);
for(const std::string& dep : deps) {
try {
@ -434,7 +439,7 @@ addons_client::install_result addons_client::do_resolve_addon_dependencies(const
// ADDON_NONE means not installed.
if(info.state == ADDON_NONE) {
missing_deps.push_back(dep);
} else if(info.state == ADDON_INSTALLED_UPGRADABLE) {
} else if(info.state == ADDON_INSTALLED_UPGRADABLE && info.installed_version != unknown_version) {
// Tight now, we don't need to distinguish the lists of missing
// and outdated addons, so just add them to missing.
missing_deps.push_back(dep);

View file

@ -270,20 +270,31 @@ bool ad_hoc_addon_fetch_session(const std::vector<std::string>& addon_ids)
}
bool return_value = true;
for(const std::string & addon_id : addon_ids) {
std::ostringstream os;
for(const std::string& addon_id : addon_ids) {
addons_list::const_iterator it = addons.find(addon_id);
if(it != addons.end()) {
const addon_info& addon = it->second;
addons_client::install_result res = client.install_addon_with_checks(addons, addon);
return_value = return_value && (res.outcome == addons_client::install_outcome::success);
// don't redownload in case it was already downloaded for being another add-on's dependency
if(!filesystem::file_exists(filesystem::get_addons_dir()+"/"+addon_id)) {
addons_client::install_result res = client.install_addon_with_checks(addons, addon);
return_value = return_value && (res.outcome == addons_client::install_outcome::success);
}
} else {
utils::string_map symbols;
symbols["addon_id"] = addon_id;
gui2::show_error_message(VGETTEXT("Could not find an add-on matching id $addon_id on the add-on server.", symbols));
if(!return_value) {
os << ", ";
}
os << addon_id;
return_value = false;
}
}
if(!return_value) {
utils::string_map symbols;
symbols["addon_ids"] = os.str();
gui2::show_error_message(VGETTEXT("Could not find add-ons matching the ids $addon_ids on the add-on server.", symbols));
}
return return_value;
} catch(const config::error& e) {

View file

@ -23,7 +23,6 @@
#include "config.hpp"
#include "deprecation.hpp"
#include "game_config.hpp"
#include "game_version.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "serialization/string_utils.hpp"
@ -510,14 +509,14 @@ std::string get_next_filename(const std::string& name, const std::string& extens
static bfs::path user_data_dir, user_config_dir, cache_dir;
static const std::string get_version_path_suffix(const version_info& version)
const std::string get_version_path_suffix(const version_info& version)
{
std::ostringstream s;
s << version.major_version() << '.' << version.minor_version();
return s.str();
}
static const std::string& get_version_path_suffix()
const std::string& get_version_path_suffix()
{
static std::string suffix;
@ -568,6 +567,9 @@ static void setup_user_data_dir()
#if defined(__APPLE__) && !defined(__IPHONEOS__)
migrate_apple_config_directory_for_unsandboxed_builds();
#endif
if(!file_exists(user_data_dir)) {
game_config::check_migration = true;
}
if(!create_directory_if_missing_recursive(user_data_dir)) {
ERR_FS << "could not open or create user data directory at " << user_data_dir.string() << '\n';
@ -1080,6 +1082,11 @@ void write_file(const std::string& fname, const std::string& data)
}
}
void copy_file(const std::string& src, const std::string& dest)
{
write_file(dest, read_file(src));
}
bool create_directory_if_missing(const std::string& dirname)
{
return create_directory_if_missing(bfs::path(dirname));

View file

@ -27,6 +27,7 @@
#include <vector>
#include "exceptions.hpp"
#include "game_version.hpp"
class config;
class game_config_view;
@ -121,14 +122,15 @@ static const blacklist_pattern_list default_blacklist{
};
/**
* Populates 'files' with all the files and
* 'dirs' with all the directories in dir.
* If files or dirs are nullptr they will not be used.
* Get a list of all files and/or directories in a given directory.
*
* mode: determines whether the entire path or just the filename is retrieved.
* filter: determines if we skip images and sounds directories
* reorder: triggers the special handling of _main.cfg and _final.cfg
* checksum: can be used to store checksum info
* @param dir The directory to examine.
* @param[out] files The files in @a dir. Won't be used if nullptr.
* @param[out] dirs The directories in @a dir. Won't be used if nullptr.
* @param mode Determines whether the entire path or just the filename is retrieved.
* @param filter Determines if we skip images and sounds directories.
* @param reorder Triggers the special handling of _main.cfg and _final.cfg.
* @param[out] checksum Can be used to store checksum info.
*/
void get_files_in_dir(const std::string &dir,
std::vector<std::string>* files,
@ -149,6 +151,8 @@ std::string get_saves_dir();
std::string get_intl_dir();
std::string get_screenshot_dir();
std::string get_addons_dir();
const std::string get_version_path_suffix(const version_info& version);
const std::string& get_version_path_suffix();
/**
* Get the next free filename using "name + number (3 digits) + extension"
@ -208,6 +212,13 @@ filesystem::scoped_istream istream_file(const std::string& fname, bool treat_fai
filesystem::scoped_ostream ostream_file(const std::string& fname, std::ios_base::openmode mode = std::ios_base::binary, bool create_directory = true);
/** Throws io_exception if an error occurs. */
void write_file(const std::string& fname, const std::string& data);
/**
* Read a file and then writes it back out.
*
* @param src The source file.
* @param dest The destination of the copied file.
*/
void copy_file(const std::string& src, const std::string& dest);
std::string read_map(const std::string& name);

View file

@ -43,6 +43,7 @@ std::string default_preferences_path = DEFAULT_PREFS_PATH;
#else
std::string default_preferences_path = "";
#endif
bool check_migration = false;
std::string wesnoth_program_dir;

View file

@ -71,6 +71,7 @@ namespace game_config
extern std::string path;
extern std::string default_preferences_path;
extern bool check_migration;
struct server_info
{

View file

@ -0,0 +1,123 @@
/*
Copyright (C) 2008 - 2021
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/dialogs/migrate_version_selection.hpp"
#include "addon/manager_ui.hpp"
#include "filesystem.hpp"
#include "game_version.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/dialogs/message.hpp"
#include "gui/widgets/listbox.hpp"
#include "gui/widgets/window.hpp"
#include "preferences/credentials.hpp"
#include "preferences/game.hpp"
#include <boost/algorithm/string.hpp>
static lg::log_domain log_version_migration{"gui/dialogs/migrate_version_selection"};
#define ERR_LOG_VERSION_MIGRATION LOG_STREAM(err, log_version_migration)
#define WRN_LOG_VERSION_MIGRATION LOG_STREAM(warn, log_version_migration)
#define LOG_LOG_VERSION_MIGRATION LOG_STREAM(info, log_version_migration)
#define DBG_LOG_VERSION_MIGRATION LOG_STREAM(debug, log_version_migration)
namespace gui2::dialogs
{
REGISTER_DIALOG(migrate_version_selection)
void migrate_version_selection::execute()
{
migrate_version_selection mig = migrate_version_selection();
if(mig.versions_.size() > 0) {
mig.show();
}
}
migrate_version_selection::migrate_version_selection()
{
version_info current_version = game_config::wesnoth_version;
std::string current_version_str = filesystem::get_version_path_suffix();
for(unsigned int i = 1; i < current_version.minor_version(); i++) {
std::string previous_version_str = std::to_string(current_version.major_version()) + "."
+ std::to_string(current_version.minor_version() - i);
std::string previous_addons_dir
= boost::replace_all_copy(filesystem::get_addons_dir(), current_version_str, previous_version_str);
if(previous_addons_dir != filesystem::get_addons_dir() && filesystem::file_exists(previous_addons_dir)) {
versions_.push_back(previous_version_str);
}
}
}
void migrate_version_selection::pre_show(window& window)
{
listbox& version_list = find_widget<listbox>(&window, "versions_listbox", false);
for(const auto& version : versions_) {
std::map<std::string, string_map> data;
string_map item_label;
item_label["label"] = version;
data["version_label"] = item_label;
version_list.add_row(data);
}
}
void migrate_version_selection::post_show(window& window)
{
if(get_retval() == gui2::OK) {
std::string current_version_str = filesystem::get_version_path_suffix();
listbox& version_list = find_widget<listbox>(&window, "versions_listbox", false);
int selected_row = version_list.get_selected_row();
std::string selected = versions_.at(selected_row);
std::string migrate_addons_dir
= boost::replace_all_copy(filesystem::get_addons_dir(), current_version_str, selected);
std::string migrate_prefs_file
= boost::replace_all_copy(filesystem::get_prefs_file(), current_version_str, selected);
std::string migrate_credentials_file
= boost::replace_all_copy(filesystem::get_credentials_file(), current_version_str, selected);
// given self-compilation and linux distros being able to do whatever they want plus command line options to
// alter locations make sure the directories/files are actually different before doing anything with them
if(migrate_addons_dir != filesystem::get_addons_dir()) {
std::vector<std::string> migrate_addons;
filesystem::get_files_in_dir(migrate_addons_dir, nullptr, &migrate_addons);
if(migrate_addons.size() > 0) {
ad_hoc_addon_fetch_session(migrate_addons);
}
}
if(migrate_prefs_file != filesystem::get_prefs_file() && filesystem::file_exists(migrate_prefs_file)) {
filesystem::copy_file(migrate_prefs_file, filesystem::get_prefs_file());
}
if(migrate_credentials_file != filesystem::get_credentials_file()
&& filesystem::file_exists(migrate_credentials_file)) {
filesystem::copy_file(migrate_credentials_file, filesystem::get_credentials_file());
}
// reload preferences and credentials
// otherwise the copied files won't be used and also will get overwritten/deleted when Wesnoth closes
preferences::load_base_prefs();
preferences::load_game_prefs();
preferences::load_credentials();
}
}
} // namespace gui2::dialogs

View file

@ -0,0 +1,39 @@
/*
Copyright (C) 2008 - 2021
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "gui/dialogs/modal_dialog.hpp"
namespace gui2::dialogs
{
/**
* @ingroup GUIWindowDefinitionWML
*
* This shows the dialog to select a previous version of Wesnoth to migrate preferences from and redownload add-ons.
*/
class migrate_version_selection : public modal_dialog
{
public:
migrate_version_selection();
static void execute();
private:
virtual void pre_show(window& window) override;
virtual void post_show(window& window) override;
virtual const std::string& window_id() const override;
std::vector<std::string> versions_;
};
} // namespace gui2::dialogs

View file

@ -72,6 +72,53 @@ namespace preferences
{
manager::manager()
: base()
{
load_game_prefs();
}
manager::~manager()
{
config campaigns;
for(const auto& elem : completed_campaigns) {
config cmp;
cmp["name"] = elem.first;
cmp["difficulty_levels"] = utils::join(elem.second);
campaigns.add_child("campaign", cmp);
}
preferences::set_child("completed_campaigns", campaigns);
preferences::set("encountered_units", utils::join(encountered_units_set));
t_translation::ter_list terrain(encountered_terrains_set.begin(), encountered_terrains_set.end());
preferences::set("encountered_terrain_list", t_translation::write_list(terrain));
/* Structure of the history
[history]
[history_id]
[line]
message = foobar
[/line]
*/
config history;
for(const auto& history_id : history_map) {
config history_id_cfg; // [history_id]
for(const std::string& line : history_id.second) {
config cfg; // [line]
cfg["message"] = line;
history_id_cfg.add_child("line", std::move(cfg));
}
history.add_child(history_id.first, history_id_cfg);
}
preferences::set_child("history", history);
history_map.clear();
encountered_units_set.clear();
encountered_terrains_set.clear();
}
void load_game_prefs()
{
set_music_volume(music_volume());
set_sound_volume(sound_volume());
@ -129,48 +176,6 @@ manager::manager()
}
}
manager::~manager()
{
config campaigns;
for(const auto& elem : completed_campaigns) {
config cmp;
cmp["name"] = elem.first;
cmp["difficulty_levels"] = utils::join(elem.second);
campaigns.add_child("campaign", cmp);
}
preferences::set_child("completed_campaigns", campaigns);
preferences::set("encountered_units", utils::join(encountered_units_set));
t_translation::ter_list terrain(encountered_terrains_set.begin(), encountered_terrains_set.end());
preferences::set("encountered_terrain_list", t_translation::write_list(terrain));
/* Structure of the history
[history]
[history_id]
[line]
message = foobar
[/line]
*/
config history;
for(const auto& history_id : history_map) {
config history_id_cfg; // [history_id]
for(const std::string& line : history_id.second) {
config cfg; // [line]
cfg["message"] = line;
history_id_cfg.add_child("line", std::move(cfg));
}
history.add_child(history_id.first, history_id_cfg);
}
preferences::set_child("history", history);
history_map.clear();
encountered_units_set.clear();
encountered_terrains_set.clear();
}
static void load_acquaintances()
{
if(acquaintances.empty()) {

View file

@ -80,6 +80,7 @@ void _set_lobby_joins(int show);
enum LOBBY_JOINS { SHOW_NONE, SHOW_FRIENDS, SHOW_ALL };
void load_game_prefs();
const std::map<std::string, acquaintance>& get_acquaintances();
const std::string get_ignored_delim();
std::map<std::string, std::string> get_acquaintances_nice(const std::string& filter);

View file

@ -84,25 +84,7 @@ base_manager::base_manager()
{
event_handler_.join_global();
try{
#ifdef DEFAULT_PREFS_PATH
filesystem::scoped_istream stream = filesystem::istream_file(filesystem::get_default_prefs_file(),false);
read(prefs, *stream);
config user_prefs;
stream = filesystem::istream_file(filesystem::get_prefs_file());
read(user_prefs, *stream);
prefs.merge_with(user_prefs);
#else
filesystem::scoped_istream stream = filesystem::istream_file(filesystem::get_prefs_file(),false);
read(prefs, *stream);
#endif
} catch(const config::error& e) {
ERR_CFG << "Error loading preference, message: "
<< e.what()
<< std::endl;
}
preferences::load_base_prefs();
preferences::load_credentials();
}
@ -250,6 +232,29 @@ config* get_prefs(){
return pointer;
}
void load_base_prefs() {
try{
#ifdef DEFAULT_PREFS_PATH
filesystem::scoped_istream stream = filesystem::istream_file(filesystem::get_default_prefs_file(),false);
read(prefs, *stream);
config user_prefs;
stream = filesystem::istream_file(filesystem::get_prefs_file());
read(user_prefs, *stream);
prefs.merge_with(user_prefs);
#else
prefs.clear();
filesystem::scoped_istream stream = filesystem::istream_file(filesystem::get_prefs_file(),false);
read(prefs, *stream);
#endif
} catch(const config::error& e) {
ERR_CFG << "Error loading preference, message: "
<< e.what()
<< std::endl;
}
}
bool show_allied_orb() {
return get("show_ally_orb", game_config::show_ally_orb);

View file

@ -63,6 +63,7 @@ namespace preferences {
void disable_preferences_save();
config* get_prefs();
void load_base_prefs();
std::string core_id();
void set_core_id(const std::string& root);

View file

@ -40,11 +40,12 @@
#undef VERSION
#endif
#define VERSION "1.15.14+dev"
// TODO: undo changes to this file before merge
#define VERSION "1.16.0"
// Used for the Windows executables' version info resource.
#define RC_VERSION_MAJOR 1
#define RC_VERSION_MINOR 15
#define RC_VERSION_REVISION 15
#define RC_VERSION_MINOR 16
#define RC_VERSION_REVISION 0
#endif

View file

@ -33,6 +33,7 @@
#include "gui/dialogs/end_credits.hpp"
#include "gui/dialogs/loading_screen.hpp"
#include "gui/dialogs/message.hpp" // for show_error_message
#include "gui/dialogs/migrate_version_selection.hpp"
#include "gui/dialogs/title_screen.hpp" // for title_screen, etc
#include "gui/gui.hpp" // for init
#include "picture.hpp" // for flush_cache, etc
@ -779,6 +780,11 @@ static int do_gameloop(const std::vector<std::string>& args)
game_config_manager config_manager(cmdline_opts);
if(game_config::check_migration) {
game_config::check_migration = false;
gui2::dialogs::migrate_version_selection::execute();
}
gui2::dialogs::loading_screen::display([&res, &config_manager, &cmdline_opts]() {
gui2::dialogs::loading_screen::progress(loading_stage::load_config);
res = config_manager.init_game_config(game_config_manager::NO_FORCE_RELOAD);