File diff r25748:2c5cea3968c5 → r25749:4905ec9578cb
src/settings.cpp
Show inline comments
 
@@ -23,6 +23,7 @@
 

	
 
#include "stdafx.h"
 
#include <array>
 
#include <charconv>
 
#include <limits>
 
#include "currency.h"
 
#include "screenshot.h"
 
@@ -81,8 +82,10 @@
 
ClientSettings _settings_client;
 
GameSettings _settings_game;     ///< Game settings of a running game or the scenario editor.
 
GameSettings _settings_newgame;  ///< Game settings for new games (updated from the intro screen).
 
VehicleDefaultSettings _old_vds; ///< Used for loading default vehicles settings from old savegames
 
std::string _config_file; ///< Configuration file of OpenTTD
 
VehicleDefaultSettings _old_vds; ///< Used for loading default vehicles settings from old savegames.
 
std::string _config_file; ///< Configuration file of OpenTTD.
 
std::string _private_file; ///< Private configuration file of OpenTTD.
 
std::string _secrets_file; ///< Secrets configuration file of OpenTTD.
 

	
 
typedef std::list<ErrorMessageData> ErrorList;
 
static ErrorList _settings_error_list; ///< Errors while loading minimal settings.
 
@@ -90,7 +93,7 @@ static ErrorList _settings_error_list; /
 
typedef span<const SettingVariant> SettingTable;
 

	
 
/**
 
 * List of all the setting tables.
 
 * List of all the generic setting tables.
 
 *
 
 * There are a few tables that are special and not processed like the rest:
 
 * - _currency_settings
 
@@ -99,11 +102,25 @@ typedef span<const SettingVariant> Setti
 
 * - _win32_settings
 
 * As such, they are not part of this list.
 
 */
 
static const SettingTable _setting_tables[] = {
 
static const SettingTable _generic_setting_tables[] = {
 
	_settings,
 
	_network_settings,
 
};
 

	
 
/**
 
 * List of all the private setting tables.
 
 */
 
static const SettingTable _private_setting_tables[] = {
 
	_network_private_settings,
 
};
 

	
 
/**
 
 * List of all the secrets setting tables.
 
 */
 
static const SettingTable _secrets_setting_tables[] = {
 
	_network_secrets_settings,
 
};
 

	
 
typedef void SettingDescProc(IniFile &ini, const SettingTable &desc, const char *grpname, void *object, bool only_startup);
 
typedef void SettingDescProcList(IniFile &ini, const char *grpname, StringList &list);
 

	
 
@@ -130,6 +147,22 @@ public:
 
};
 

	
 
/**
 
 * Ini-file versions.
 
 *
 
 * Sometimes we move settings between different ini-files, as we need to know
 
 * when we have to load/remove it from the old versus reading it from the new
 
 * location. These versions assist with situations like that.
 
 */
 
enum IniFileVersion : uint32 {
 
	IFV_0,               ///< 0  All versions prior to introduction.
 
	IFV_PRIVATE_SECRETS, ///< 1  PR#9298  Moving of settings from openttd.cfg to private.cfg / secrets.cfg.
 

	
 
	IFV_MAX_VERSION,     ///< Highest possible ini-file version.
 
};
 

	
 
const uint16 INIFILE_VERSION = (IniFileVersion)(IFV_MAX_VERSION - 1); ///< Current ini-file version of OpenTTD.
 

	
 
/**
 
 * Helper to convert the type of the iterated settings description to a pointer to it.
 
 * @param desc The type of the iterator of the value in SettingTable.
 
 * @return The actual pointer to SettingDesc.
 
@@ -1473,6 +1506,20 @@ static GRFConfig *GRFLoadConfig(IniFile 
 
	return first;
 
}
 

	
 
static IniFileVersion LoadVersionFromConfig(IniFile &ini)
 
{
 
	IniGroup *group = ini.GetGroup("version");
 

	
 
	auto version_number = group->GetItem("ini_version", false);
 
	/* Older ini-file versions don't have this key yet. */
 
	if (version_number == nullptr || !version_number->value.has_value()) return IFV_0;
 

	
 
	uint32 version = 0;
 
	std::from_chars(version_number->value->data(), version_number->value->data() + version_number->value->size(), version);
 

	
 
	return static_cast<IniFileVersion>(version);
 
}
 

	
 
static void AISaveConfig(IniFile &ini, const char *grpname)
 
{
 
	IniGroup *group = ini.GetGroup(grpname);
 
@@ -1526,6 +1573,7 @@ static void SaveVersionInConfig(IniFile 
 
	IniGroup *group = ini.GetGroup("version");
 
	group->GetItem("version_string", true)->SetValue(_openttd_revision);
 
	group->GetItem("version_number", true)->SetValue(fmt::format("{:08X}", _openttd_newgrf_version));
 
	group->GetItem("ini_version", true)->SetValue(std::to_string(INIFILE_VERSION));
 
}
 

	
 
/* Save a GRF configuration to the given group name */
 
@@ -1549,25 +1597,58 @@ static void GRFSaveConfig(IniFile &ini, 
 
}
 

	
 
/* Common handler for saving/loading variables to the configuration file */
 
static void HandleSettingDescs(IniFile &ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool only_startup = false)
 
static void HandleSettingDescs(IniFile &generic_ini, IniFile &private_ini, IniFile &secrets_ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool only_startup = false)
 
{
 
	proc(ini, _misc_settings,    "misc",  nullptr, only_startup);
 
	proc(generic_ini, _misc_settings, "misc", nullptr, only_startup);
 
#if defined(_WIN32) && !defined(DEDICATED)
 
	proc(ini, _win32_settings,   "win32", nullptr, only_startup);
 
	proc(generic_ini, _win32_settings, "win32", nullptr, only_startup);
 
#endif /* _WIN32 */
 

	
 
	for (auto &table : _setting_tables) {
 
		/* The name "patches" is a fallback, as every setting should sets its own group. */
 
		proc(ini, table, "patches", &_settings_newgame, only_startup);
 
	/* The name "patches" is a fallback, as every setting should sets its own group. */
 

	
 
	for (auto &table : _generic_setting_tables) {
 
		proc(generic_ini, table, "patches", &_settings_newgame, only_startup);
 
	}
 
	for (auto &table : _private_setting_tables) {
 
		proc(private_ini, table, "patches", &_settings_newgame, only_startup);
 
	}
 
	for (auto &table : _secrets_setting_tables) {
 
		proc(secrets_ini, table, "patches", &_settings_newgame, only_startup);
 
	}
 

	
 
	proc(ini, _currency_settings,"currency", &_custom_currency, only_startup);
 
	proc(ini, _company_settings, "company",  &_settings_client.company, only_startup);
 
	proc(generic_ini, _currency_settings, "currency", &_custom_currency, only_startup);
 
	proc(generic_ini, _company_settings, "company", &_settings_client.company, only_startup);
 

	
 
	if (!only_startup) {
 
		proc_list(ini, "server_bind_addresses", _network_bind_list);
 
		proc_list(ini, "servers", _network_host_list);
 
		proc_list(ini, "bans",    _network_ban_list);
 
		proc_list(private_ini, "server_bind_addresses", _network_bind_list);
 
		proc_list(private_ini, "servers", _network_host_list);
 
		proc_list(private_ini, "bans", _network_ban_list);
 
	}
 
}
 

	
 
/**
 
 * Remove all entries from a settings table from an ini-file.
 
 *
 
 * This is only useful if those entries are moved to another file, and you
 
 * want to clean up what is left behind.
 
 *
 
 * @param ini The ini file to remove the entries from.
 
 * @param table The table to look for entries to remove.
 
 */
 
static void RemoveEntriesFromIni(IniFile &ini, const SettingTable &table)
 
{
 
	for (auto &desc : table) {
 
		const SettingDesc *sd = GetSettingDesc(desc);
 

	
 
		/* For settings.xx.yy load the settings from [xx] yy = ? */
 
		std::string s{ sd->name };
 
		auto sc = s.find('.');
 
		if (sc == std::string::npos) continue;
 

	
 
		IniGroup *group = ini.GetGroup(s.substr(0, sc));
 
		s = s.substr(sc + 1);
 

	
 
		group->RemoveItem(s);
 
	}
 
}
 

	
 
@@ -1577,20 +1658,30 @@ static void HandleSettingDescs(IniFile &
 
 */
 
void LoadFromConfig(bool startup)
 
{
 
	ConfigIniFile ini(_config_file);
 
	ConfigIniFile generic_ini(_config_file);
 
	ConfigIniFile private_ini(_private_file);
 
	ConfigIniFile secrets_ini(_secrets_file);
 

	
 
	if (!startup) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one
 

	
 
	IniFileVersion generic_version = LoadVersionFromConfig(generic_ini);
 

	
 
	/* Before the split of private/secrets, we have to look in the generic for these settings. */
 
	if (generic_version < IFV_PRIVATE_SECRETS) {
 
		HandleSettingDescs(generic_ini, generic_ini, generic_ini, IniLoadSettings, IniLoadSettingList, startup);
 
	} else {
 
		HandleSettingDescs(generic_ini, private_ini, secrets_ini, IniLoadSettings, IniLoadSettingList, startup);
 
	}
 

	
 
	/* Load basic settings only during bootstrap, load other settings not during bootstrap */
 
	HandleSettingDescs(ini, IniLoadSettings, IniLoadSettingList, startup);
 

	
 
	if (!startup) {
 
		_grfconfig_newgame = GRFLoadConfig(ini, "newgrf", false);
 
		_grfconfig_static  = GRFLoadConfig(ini, "newgrf-static", true);
 
		AILoadConfig(ini, "ai_players");
 
		GameLoadConfig(ini, "game_scripts");
 
		_grfconfig_newgame = GRFLoadConfig(generic_ini, "newgrf", false);
 
		_grfconfig_static  = GRFLoadConfig(generic_ini, "newgrf-static", true);
 
		AILoadConfig(generic_ini, "ai_players");
 
		GameLoadConfig(generic_ini, "game_scripts");
 

	
 
		PrepareOldDiffCustom();
 
		IniLoadSettings(ini, _gameopt_settings, "gameopt", &_settings_newgame, false);
 
		IniLoadSettings(generic_ini, _gameopt_settings, "gameopt", &_settings_newgame, false);
 
		HandleOldDiffCustom(false);
 

	
 
		ValidateSettings();
 
@@ -1605,20 +1696,52 @@ void LoadFromConfig(bool startup)
 
/** Save the values to the configuration file */
 
void SaveToConfig()
 
{
 
	ConfigIniFile ini(_config_file);
 
	ConfigIniFile generic_ini(_config_file);
 
	ConfigIniFile private_ini(_private_file);
 
	ConfigIniFile secrets_ini(_secrets_file);
 

	
 
	IniFileVersion generic_version = LoadVersionFromConfig(generic_ini);
 

	
 
	/* Remove some obsolete groups. These have all been loaded into other groups. */
 
	ini.RemoveGroup("patches");
 
	ini.RemoveGroup("yapf");
 
	ini.RemoveGroup("gameopt");
 
	/* If we newly create the private/secrets file, add a dummy group on top
 
	 * just so we can add a comment before it (that is how IniFile works).
 
	 * This to explain what the file is about. After doing it once, never touch
 
	 * it again, as otherwise we might be reverting user changes. */
 
	if (!private_ini.GetGroup("private", false)) private_ini.GetGroup("private")->comment = "; This file possibly contains private information which can identify you as person.\n";
 
	if (!secrets_ini.GetGroup("secrets", false)) secrets_ini.GetGroup("secrets")->comment = "; Do not share this file with others, not even if they claim to be technical support.\n; This file contains saved passwords and other secrets that should remain private to you!\n";
 

	
 
	if (generic_version == IFV_0) {
 
		/* Remove some obsolete groups. These have all been loaded into other groups. */
 
		generic_ini.RemoveGroup("patches");
 
		generic_ini.RemoveGroup("yapf");
 
		generic_ini.RemoveGroup("gameopt");
 

	
 
	HandleSettingDescs(ini, IniSaveSettings, IniSaveSettingList);
 
	GRFSaveConfig(ini, "newgrf", _grfconfig_newgame);
 
	GRFSaveConfig(ini, "newgrf-static", _grfconfig_static);
 
	AISaveConfig(ini, "ai_players");
 
	GameSaveConfig(ini, "game_scripts");
 
	SaveVersionInConfig(ini);
 
	ini.SaveToDisk(_config_file);
 
		/* Remove all settings from the generic ini that are now in the private ini. */
 
		generic_ini.RemoveGroup("server_bind_addresses");
 
		generic_ini.RemoveGroup("servers");
 
		generic_ini.RemoveGroup("bans");
 
		for (auto &table : _private_setting_tables) {
 
			RemoveEntriesFromIni(generic_ini, table);
 
		}
 

	
 
		/* Remove all settings from the generic ini that are now in the secrets ini. */
 
		for (auto &table : _secrets_setting_tables) {
 
			RemoveEntriesFromIni(generic_ini, table);
 
		}
 
	}
 

	
 
	HandleSettingDescs(generic_ini, private_ini, secrets_ini, IniSaveSettings, IniSaveSettingList);
 
	GRFSaveConfig(generic_ini, "newgrf", _grfconfig_newgame);
 
	GRFSaveConfig(generic_ini, "newgrf-static", _grfconfig_static);
 
	AISaveConfig(generic_ini, "ai_players");
 
	GameSaveConfig(generic_ini, "game_scripts");
 

	
 
	SaveVersionInConfig(generic_ini);
 
	SaveVersionInConfig(private_ini);
 
	SaveVersionInConfig(secrets_ini);
 

	
 
	generic_ini.SaveToDisk(_config_file);
 
	private_ini.SaveToDisk(_private_file);
 
	secrets_ini.SaveToDisk(_secrets_file);
 
}
 

	
 
/**
 
@@ -1778,7 +1901,15 @@ static const SettingDesc *GetCompanySett
 
 */
 
const SettingDesc *GetSettingFromName(const std::string_view name)
 
{
 
	for (auto &table : _setting_tables) {
 
	for (auto &table : _generic_setting_tables) {
 
		auto sd = GetSettingFromName(name, table);
 
		if (sd != nullptr) return sd;
 
	}
 
	for (auto &table : _private_setting_tables) {
 
		auto sd = GetSettingFromName(name, table);
 
		if (sd != nullptr) return sd;
 
	}
 
	for (auto &table : _secrets_setting_tables) {
 
		auto sd = GetSettingFromName(name, table);
 
		if (sd != nullptr) return sd;
 
	}
 
@@ -2014,6 +2145,18 @@ void IConsoleGetSetting(const char *name
 
	}
 
}
 

	
 
static void IConsoleListSettingsTable(const SettingTable &table, const char *prefilter)
 
{
 
	for (auto &desc : table) {
 
		const SettingDesc *sd = GetSettingDesc(desc);
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (prefilter != nullptr && sd->name.find(prefilter) == std::string::npos) continue;
 
		char value[80];
 
		sd->FormatValue(value, lastof(value), &GetGameSettings());
 
		IConsolePrint(CC_DEFAULT, "{} = {}", sd->name, value);
 
	}
 
}
 

	
 
/**
 
 * List all settings and their value to the console
 
 *
 
@@ -2023,15 +2166,14 @@ void IConsoleListSettings(const char *pr
 
{
 
	IConsolePrint(CC_HELP, "All settings with their current value:");
 

	
 
	for (auto &table : _setting_tables) {
 
		for (auto &desc : table) {
 
			const SettingDesc *sd = GetSettingDesc(desc);
 
			if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
			if (prefilter != nullptr && sd->name.find(prefilter) == std::string::npos) continue;
 
			char value[80];
 
			sd->FormatValue(value, lastof(value), &GetGameSettings());
 
			IConsolePrint(CC_DEFAULT, "{} = {}", sd->name, value);
 
		}
 
	for (auto &table : _generic_setting_tables) {
 
		IConsoleListSettingsTable(table, prefilter);
 
	}
 
	for (auto &table : _private_setting_tables) {
 
		IConsoleListSettingsTable(table, prefilter);
 
	}
 
	for (auto &table : _secrets_setting_tables) {
 
		IConsoleListSettingsTable(table, prefilter);
 
	}
 

	
 
	IConsolePrint(CC_HELP, "Use 'setting' command to change a value.");