mirror of
https://github.com/minetest/minetest.git
synced 2024-12-24 15:12:23 +01:00
Settings: Proper priority hierarchy
Remove old defaults system Introduce priority-based fallback list Use new functions for map_meta special functions Change groups to use end tags Unittest changes: * Adapt unittest to the new code * Compare Settings objects
This commit is contained in:
parent
5e9dd1667b
commit
37a05ec8d6
@ -329,18 +329,16 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
|
||||
}
|
||||
}
|
||||
|
||||
// Override defaults with those provided by the game.
|
||||
// We clear and reload the defaults because the defaults
|
||||
// might have been overridden by other subgame config
|
||||
// files that were loaded before.
|
||||
g_settings->clearDefaults();
|
||||
set_default_settings(g_settings);
|
||||
Settings *game_settings = Settings::getLayer(SL_GAME);
|
||||
const bool new_game_settings = (game_settings == nullptr);
|
||||
if (new_game_settings) {
|
||||
// Called by main-menu without a Server instance running
|
||||
// -> create and free manually
|
||||
game_settings = Settings::createLayer(SL_GAME);
|
||||
}
|
||||
|
||||
Settings game_defaults;
|
||||
getGameMinetestConfig(gamespec.path, game_defaults);
|
||||
game_defaults.removeSecureSettings();
|
||||
|
||||
g_settings->overrideDefaults(&game_defaults);
|
||||
getGameMinetestConfig(gamespec.path, *game_settings);
|
||||
game_settings->removeSecureSettings();
|
||||
|
||||
infostream << "Initializing world at " << final_path << std::endl;
|
||||
|
||||
@ -381,4 +379,8 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
|
||||
|
||||
fs::safeWriteToFile(map_meta_path, oss.str());
|
||||
}
|
||||
|
||||
// The Settings object is no longer needed for created worlds
|
||||
if (new_game_settings)
|
||||
delete game_settings;
|
||||
}
|
||||
|
@ -122,18 +122,17 @@ void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
|
||||
args.set("name", p->m_name);
|
||||
|
||||
// This should not happen
|
||||
assert(m_sao);
|
||||
args.setU16("hp", p->m_sao->getHP());
|
||||
args.setV3F("position", p->m_sao->getBasePosition());
|
||||
args.setFloat("pitch", p->m_sao->getLookPitch());
|
||||
args.setFloat("yaw", p->m_sao->getRotation().Y);
|
||||
args.setU16("breath", p->m_sao->getBreath());
|
||||
PlayerSAO *sao = p->getPlayerSAO();
|
||||
assert(sao);
|
||||
args.setU16("hp", sao->getHP());
|
||||
args.setV3F("position", sao->getBasePosition());
|
||||
args.setFloat("pitch", sao->getLookPitch());
|
||||
args.setFloat("yaw", sao->getRotation().Y);
|
||||
args.setU16("breath", sao->getBreath());
|
||||
|
||||
std::string extended_attrs;
|
||||
{
|
||||
// serializeExtraAttributes
|
||||
PlayerSAO *sao = p->getPlayerSAO();
|
||||
assert(sao);
|
||||
Json::Value json_root;
|
||||
|
||||
const StringMap &attrs = sao->getMeta().getStrings();
|
||||
|
@ -38,8 +38,8 @@ public:
|
||||
void listPlayers(std::vector<std::string> &res);
|
||||
|
||||
private:
|
||||
void deSerialize(RemotePlayer *p, std::istream &is,
|
||||
const std::string &playername, PlayerSAO *sao);
|
||||
void deSerialize(RemotePlayer *p, std::istream &is, const std::string &playername,
|
||||
PlayerSAO *sao);
|
||||
/*
|
||||
serialize() writes a bunch of text that can contain
|
||||
any characters except a '\0', and such an ending that
|
||||
|
@ -27,8 +27,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "mapgen/mapgen.h" // Mapgen::setDefaultSettings
|
||||
#include "util/string.h"
|
||||
|
||||
void set_default_settings(Settings *settings)
|
||||
void set_default_settings()
|
||||
{
|
||||
Settings *settings = Settings::createLayer(SL_DEFAULTS);
|
||||
|
||||
// Client and server
|
||||
settings->setDefault("language", "");
|
||||
settings->setDefault("name", "");
|
||||
|
@ -25,11 +25,4 @@ class Settings;
|
||||
* initialize basic default settings
|
||||
* @param settings pointer to settings
|
||||
*/
|
||||
void set_default_settings(Settings *settings);
|
||||
|
||||
/**
|
||||
* override a default settings by settings from another settings element
|
||||
* @param settings target settings pointer
|
||||
* @param from source settings pointer
|
||||
*/
|
||||
void override_default_settings(Settings *settings, Settings *from);
|
||||
void set_default_settings();
|
||||
|
@ -248,7 +248,7 @@ bool GUIKeyChangeMenu::acceptInput()
|
||||
{
|
||||
for (key_setting *k : key_settings) {
|
||||
std::string default_key;
|
||||
g_settings->getDefaultNoEx(k->setting_name, default_key);
|
||||
Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
|
||||
|
||||
if (k->key.sym() != default_key)
|
||||
g_settings->set(k->setting_name, k->key.sym());
|
||||
|
@ -487,12 +487,15 @@ static bool create_userdata_path()
|
||||
static bool init_common(const Settings &cmd_args, int argc, char *argv[])
|
||||
{
|
||||
startup_message();
|
||||
set_default_settings(g_settings);
|
||||
set_default_settings();
|
||||
|
||||
// Initialize sockets
|
||||
sockets_init();
|
||||
atexit(sockets_cleanup);
|
||||
|
||||
// Initialize g_settings
|
||||
Settings::createLayer(SL_GLOBAL);
|
||||
|
||||
if (!read_config_file(cmd_args))
|
||||
return false;
|
||||
|
||||
@ -524,6 +527,7 @@ static bool read_config_file(const Settings &cmd_args)
|
||||
// Path of configuration file in use
|
||||
sanity_check(g_settings_path == ""); // Sanity check
|
||||
|
||||
|
||||
if (cmd_args.exists("config")) {
|
||||
bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
|
||||
if (!r) {
|
||||
|
@ -1184,7 +1184,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
||||
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
|
||||
EmergeManager *emerge, MetricsBackend *mb):
|
||||
Map(gamedef),
|
||||
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
|
||||
settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
|
||||
m_emerge(emerge)
|
||||
{
|
||||
verbosestream<<FUNCTION_NAME<<std::endl;
|
||||
|
@ -25,17 +25,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include "map_settings_manager.h"
|
||||
|
||||
MapSettingsManager::MapSettingsManager(Settings *user_settings,
|
||||
const std::string &map_meta_path):
|
||||
m_map_meta_path(map_meta_path),
|
||||
m_map_settings(new Settings()),
|
||||
m_user_settings(user_settings)
|
||||
MapSettingsManager::MapSettingsManager(const std::string &map_meta_path):
|
||||
m_map_meta_path(map_meta_path)
|
||||
{
|
||||
assert(m_user_settings != NULL);
|
||||
|
||||
Mapgen::setDefaultSettings(m_map_settings);
|
||||
// This inherits the combined defaults provided by loadGameConfAndInitWorld.
|
||||
m_map_settings->overrideDefaults(user_settings);
|
||||
m_map_settings = Settings::createLayer(SL_MAP, "[end_of_params]");
|
||||
Mapgen::setDefaultSettings(Settings::getLayer(SL_DEFAULTS));
|
||||
}
|
||||
|
||||
|
||||
@ -49,22 +43,23 @@ MapSettingsManager::~MapSettingsManager()
|
||||
bool MapSettingsManager::getMapSetting(
|
||||
const std::string &name, std::string *value_out)
|
||||
{
|
||||
// Get from map_meta.txt, then try from all other sources
|
||||
if (m_map_settings->getNoEx(name, *value_out))
|
||||
return true;
|
||||
|
||||
// Compatibility kludge
|
||||
if (m_user_settings == g_settings && name == "seed")
|
||||
return m_user_settings->getNoEx("fixed_map_seed", *value_out);
|
||||
if (name == "seed")
|
||||
return Settings::getLayer(SL_GLOBAL)->getNoEx("fixed_map_seed", *value_out);
|
||||
|
||||
return m_user_settings->getNoEx(name, *value_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool MapSettingsManager::getMapSettingNoiseParams(
|
||||
const std::string &name, NoiseParams *value_out)
|
||||
{
|
||||
return m_map_settings->getNoiseParams(name, *value_out) ||
|
||||
m_user_settings->getNoiseParams(name, *value_out);
|
||||
// TODO: Rename to "getNoiseParams"
|
||||
return m_map_settings->getNoiseParams(name, *value_out);
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +72,7 @@ bool MapSettingsManager::setMapSetting(
|
||||
if (override_meta)
|
||||
m_map_settings->set(name, value);
|
||||
else
|
||||
m_map_settings->setDefault(name, value);
|
||||
Settings::getLayer(SL_GLOBAL)->set(name, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -89,7 +84,11 @@ bool MapSettingsManager::setMapSettingNoiseParams(
|
||||
if (mapgen_params)
|
||||
return false;
|
||||
|
||||
m_map_settings->setNoiseParams(name, *value, !override_meta);
|
||||
if (override_meta)
|
||||
m_map_settings->setNoiseParams(name, *value);
|
||||
else
|
||||
Settings::getLayer(SL_GLOBAL)->setNoiseParams(name, *value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -104,8 +103,8 @@ bool MapSettingsManager::loadMapMeta()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_map_settings->parseConfigLines(is, "[end_of_params]")) {
|
||||
errorstream << "loadMapMeta: [end_of_params] not found!" << std::endl;
|
||||
if (!m_map_settings->parseConfigLines(is)) {
|
||||
errorstream << "loadMapMeta: Format error. '[end_of_params]' missing?" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -116,28 +115,22 @@ bool MapSettingsManager::loadMapMeta()
|
||||
bool MapSettingsManager::saveMapMeta()
|
||||
{
|
||||
// If mapgen params haven't been created yet; abort
|
||||
if (!mapgen_params)
|
||||
if (!mapgen_params) {
|
||||
errorstream << "saveMapMeta: mapgen_params not present!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Paths set up by subgames.cpp, but not in unittests
|
||||
if (!fs::CreateAllDirs(fs::RemoveLastPathComponent(m_map_meta_path))) {
|
||||
errorstream << "saveMapMeta: could not create dirs to "
|
||||
<< m_map_meta_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ostringstream oss(std::ios_base::binary);
|
||||
Settings conf;
|
||||
mapgen_params->MapgenParams::writeParams(m_map_settings);
|
||||
mapgen_params->writeParams(m_map_settings);
|
||||
|
||||
mapgen_params->MapgenParams::writeParams(&conf);
|
||||
mapgen_params->writeParams(&conf);
|
||||
conf.writeLines(oss);
|
||||
|
||||
// NOTE: If there are ever types of map settings other than
|
||||
// those relating to map generation, save them here
|
||||
|
||||
oss << "[end_of_params]\n";
|
||||
|
||||
if (!fs::safeWriteToFile(m_map_meta_path, oss.str())) {
|
||||
if (!m_map_settings->updateConfigFile(m_map_meta_path.c_str())) {
|
||||
errorstream << "saveMapMeta: could not write "
|
||||
<< m_map_meta_path << std::endl;
|
||||
return false;
|
||||
@ -152,23 +145,21 @@ MapgenParams *MapSettingsManager::makeMapgenParams()
|
||||
if (mapgen_params)
|
||||
return mapgen_params;
|
||||
|
||||
assert(m_user_settings != NULL);
|
||||
assert(m_map_settings != NULL);
|
||||
|
||||
// At this point, we have (in order of precedence):
|
||||
// 1). m_mapgen_settings->m_settings containing map_meta.txt settings or
|
||||
// 1). SL_MAP containing map_meta.txt settings or
|
||||
// explicit overrides from scripts
|
||||
// 2). m_mapgen_settings->m_defaults containing script-set mgparams without
|
||||
// overrides
|
||||
// 3). g_settings->m_settings containing all user-specified config file
|
||||
// 2). SL_GLOBAL containing all user-specified config file
|
||||
// settings
|
||||
// 4). g_settings->m_defaults containing any low-priority settings from
|
||||
// 3). SL_DEFAULTS containing any low-priority settings from
|
||||
// scripts, e.g. mods using Lua as an enhanced config file)
|
||||
|
||||
// Now, get the mapgen type so we can create the appropriate MapgenParams
|
||||
std::string mg_name;
|
||||
MapgenType mgtype = getMapSetting("mg_name", &mg_name) ?
|
||||
Mapgen::getMapgenType(mg_name) : MAPGEN_DEFAULT;
|
||||
|
||||
if (mgtype == MAPGEN_INVALID) {
|
||||
errorstream << "EmergeManager: mapgen '" << mg_name <<
|
||||
"' not valid; falling back to " <<
|
||||
|
@ -44,8 +44,7 @@ struct MapgenParams;
|
||||
*/
|
||||
class MapSettingsManager {
|
||||
public:
|
||||
MapSettingsManager(Settings *user_settings,
|
||||
const std::string &map_meta_path);
|
||||
MapSettingsManager(const std::string &map_meta_path);
|
||||
~MapSettingsManager();
|
||||
|
||||
// Finalized map generation parameters
|
||||
@ -71,6 +70,6 @@ public:
|
||||
|
||||
private:
|
||||
std::string m_map_meta_path;
|
||||
// TODO: Rename to "m_settings"
|
||||
Settings *m_map_settings;
|
||||
Settings *m_user_settings;
|
||||
};
|
||||
|
@ -140,7 +140,6 @@ public:
|
||||
void onSuccessfulSave();
|
||||
|
||||
private:
|
||||
|
||||
PlayerSAO *m_sao = nullptr;
|
||||
bool m_dirty = false;
|
||||
|
||||
|
@ -982,7 +982,7 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L)
|
||||
|
||||
bool set_default = !lua_isboolean(L, 3) || readParam<bool>(L, 3);
|
||||
|
||||
g_settings->setNoiseParams(name, np, set_default);
|
||||
Settings::getLayer(set_default ? SL_DEFAULTS : SL_GLOBAL)->setNoiseParams(name, np);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ int LuaSettings::l_set_np_group(lua_State *L)
|
||||
|
||||
SET_SECURITY_CHECK(L, key);
|
||||
|
||||
o->m_settings->setNoiseParams(key, value, false);
|
||||
o->m_settings->setNoiseParams(key, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
extern "C" {
|
||||
#include "lualib.h"
|
||||
}
|
||||
|
||||
#include "settings.h"
|
||||
#define MAINMENU_NUM_ASYNC_THREADS 4
|
||||
|
||||
|
||||
|
@ -351,6 +351,7 @@ Server::~Server()
|
||||
// Deinitialize scripting
|
||||
infostream << "Server: Deinitializing scripting" << std::endl;
|
||||
delete m_script;
|
||||
delete m_game_settings;
|
||||
|
||||
while (!m_unsent_map_edit_queue.empty()) {
|
||||
delete m_unsent_map_edit_queue.front();
|
||||
@ -368,6 +369,8 @@ void Server::init()
|
||||
infostream << "- world: " << m_path_world << std::endl;
|
||||
infostream << "- game: " << m_gamespec.path << std::endl;
|
||||
|
||||
m_game_settings = Settings::createLayer(SL_GAME);
|
||||
|
||||
// Create world if it doesn't exist
|
||||
try {
|
||||
loadGameConfAndInitWorld(m_path_world,
|
||||
|
@ -524,6 +524,7 @@ private:
|
||||
u16 m_max_chatmessage_length;
|
||||
// For "dedicated" server list flag
|
||||
bool m_dedicated;
|
||||
Settings *m_game_settings = nullptr;
|
||||
|
||||
// Thread can set; step() will throw as ServerError
|
||||
MutexedVariable<std::string> m_async_fatal_error;
|
||||
|
@ -632,7 +632,7 @@ void ServerEnvironment::saveMeta()
|
||||
// Open file and serialize
|
||||
std::ostringstream ss(std::ios_base::binary);
|
||||
|
||||
Settings args;
|
||||
Settings args("EnvArgsEnd");
|
||||
args.setU64("game_time", m_game_time);
|
||||
args.setU64("time_of_day", getTimeOfDay());
|
||||
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
|
||||
@ -641,7 +641,6 @@ void ServerEnvironment::saveMeta()
|
||||
m_lbm_mgr.createIntroductionTimesString());
|
||||
args.setU64("day_count", m_day_count);
|
||||
args.writeLines(ss);
|
||||
ss<<"EnvArgsEnd\n";
|
||||
|
||||
if(!fs::safeWriteToFile(path, ss.str()))
|
||||
{
|
||||
@ -676,9 +675,9 @@ void ServerEnvironment::loadMeta()
|
||||
throw SerializationError("Couldn't load env meta");
|
||||
}
|
||||
|
||||
Settings args;
|
||||
Settings args("EnvArgsEnd");
|
||||
|
||||
if (!args.parseConfigLines(is, "EnvArgsEnd")) {
|
||||
if (!args.parseConfigLines(is)) {
|
||||
throw SerializationError("ServerEnvironment::loadMeta(): "
|
||||
"EnvArgsEnd not found!");
|
||||
}
|
||||
|
284
src/settings.cpp
284
src/settings.cpp
@ -33,27 +33,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
static Settings main_settings;
|
||||
Settings *g_settings = &main_settings;
|
||||
Settings *g_settings = nullptr;
|
||||
std::string g_settings_path;
|
||||
|
||||
Settings::~Settings()
|
||||
Settings *Settings::s_layers[SL_TOTAL_COUNT] = {0}; // Zeroed by compiler
|
||||
std::unordered_map<std::string, const FlagDesc *> Settings::s_flags;
|
||||
|
||||
|
||||
Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag)
|
||||
{
|
||||
clear();
|
||||
if ((int)sl < 0 || sl >= SL_TOTAL_COUNT)
|
||||
throw new BaseException("Invalid settings layer");
|
||||
|
||||
Settings *&pos = s_layers[(size_t)sl];
|
||||
if (pos)
|
||||
throw new BaseException("Setting layer " + std::to_string(sl) + " already exists");
|
||||
|
||||
pos = new Settings(end_tag);
|
||||
pos->m_settingslayer = sl;
|
||||
|
||||
if (sl == SL_GLOBAL)
|
||||
g_settings = pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
Settings & Settings::operator += (const Settings &other)
|
||||
Settings *Settings::getLayer(SettingsLayer sl)
|
||||
{
|
||||
if (&other == this)
|
||||
return *this;
|
||||
sanity_check((int)sl >= 0 && sl < SL_TOTAL_COUNT);
|
||||
return s_layers[(size_t)sl];
|
||||
}
|
||||
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
MutexAutoLock lock2(other.m_mutex);
|
||||
|
||||
updateNoLock(other);
|
||||
if (m_settingslayer < SL_TOTAL_COUNT)
|
||||
s_layers[(size_t)m_settingslayer] = nullptr;
|
||||
|
||||
return *this;
|
||||
// Compatibility
|
||||
if (m_settingslayer == SL_GLOBAL)
|
||||
g_settings = nullptr;
|
||||
|
||||
clearNoLock();
|
||||
}
|
||||
|
||||
|
||||
@ -62,11 +85,15 @@ Settings & Settings::operator = (const Settings &other)
|
||||
if (&other == this)
|
||||
return *this;
|
||||
|
||||
FATAL_ERROR_IF(m_settingslayer != SL_TOTAL_COUNT && other.m_settingslayer != SL_TOTAL_COUNT,
|
||||
("Tried to copy unique Setting layer " + std::to_string(m_settingslayer)).c_str());
|
||||
|
||||
MutexAutoLock lock(m_mutex);
|
||||
MutexAutoLock lock2(other.m_mutex);
|
||||
|
||||
clearNoLock();
|
||||
updateNoLock(other);
|
||||
m_settings = other.m_settings;
|
||||
m_callbacks = other.m_callbacks;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -130,11 +157,11 @@ bool Settings::readConfigFile(const char *filename)
|
||||
if (!is.good())
|
||||
return false;
|
||||
|
||||
return parseConfigLines(is, "");
|
||||
return parseConfigLines(is);
|
||||
}
|
||||
|
||||
|
||||
bool Settings::parseConfigLines(std::istream &is, const std::string &end)
|
||||
bool Settings::parseConfigLines(std::istream &is)
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
@ -142,7 +169,7 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
|
||||
|
||||
while (is.good()) {
|
||||
std::getline(is, line);
|
||||
SettingsParseEvent event = parseConfigObject(line, end, name, value);
|
||||
SettingsParseEvent event = parseConfigObject(line, name, value);
|
||||
|
||||
switch (event) {
|
||||
case SPE_NONE:
|
||||
@ -155,8 +182,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
|
||||
case SPE_END:
|
||||
return true;
|
||||
case SPE_GROUP: {
|
||||
Settings *group = new Settings;
|
||||
if (!group->parseConfigLines(is, "}")) {
|
||||
Settings *group = new Settings("}");
|
||||
if (!group->parseConfigLines(is)) {
|
||||
delete group;
|
||||
return false;
|
||||
}
|
||||
@ -169,7 +196,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
|
||||
}
|
||||
}
|
||||
|
||||
return end.empty();
|
||||
// false (failure) if end tag not found
|
||||
return m_end_tag.empty();
|
||||
}
|
||||
|
||||
|
||||
@ -179,6 +207,13 @@ void Settings::writeLines(std::ostream &os, u32 tab_depth) const
|
||||
|
||||
for (const auto &setting_it : m_settings)
|
||||
printEntry(os, setting_it.first, setting_it.second, tab_depth);
|
||||
|
||||
if (!m_end_tag.empty()) {
|
||||
for (u32 i = 0; i < tab_depth; i++)
|
||||
os << "\t";
|
||||
|
||||
os << m_end_tag << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -193,9 +228,7 @@ void Settings::printEntry(std::ostream &os, const std::string &name,
|
||||
|
||||
entry.group->writeLines(os, tab_depth + 1);
|
||||
|
||||
for (u32 i = 0; i != tab_depth; i++)
|
||||
os << "\t";
|
||||
os << "}\n";
|
||||
// Closing bracket handled by writeLines
|
||||
} else {
|
||||
os << name << " = ";
|
||||
|
||||
@ -207,8 +240,7 @@ void Settings::printEntry(std::ostream &os, const std::string &name,
|
||||
}
|
||||
|
||||
|
||||
bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
|
||||
const std::string &end, u32 tab_depth)
|
||||
bool Settings::updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth)
|
||||
{
|
||||
SettingEntries::const_iterator it;
|
||||
std::set<std::string> present_entries;
|
||||
@ -220,11 +252,11 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
|
||||
// in the object if existing
|
||||
while (is.good() && !end_found) {
|
||||
std::getline(is, line);
|
||||
SettingsParseEvent event = parseConfigObject(line, end, name, value);
|
||||
SettingsParseEvent event = parseConfigObject(line, name, value);
|
||||
|
||||
switch (event) {
|
||||
case SPE_END:
|
||||
os << line << (is.eof() ? "" : "\n");
|
||||
// Skip end tag. Append later.
|
||||
end_found = true;
|
||||
break;
|
||||
case SPE_MULTILINE:
|
||||
@ -252,14 +284,13 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
|
||||
if (it != m_settings.end() && it->second.is_group) {
|
||||
os << line << "\n";
|
||||
sanity_check(it->second.group != NULL);
|
||||
was_modified |= it->second.group->updateConfigObject(is, os,
|
||||
"}", tab_depth + 1);
|
||||
was_modified |= it->second.group->updateConfigObject(is, os, tab_depth + 1);
|
||||
} else if (it == m_settings.end()) {
|
||||
// Remove by skipping
|
||||
was_modified = true;
|
||||
Settings removed_group; // Move 'is' to group end
|
||||
Settings removed_group("}"); // Move 'is' to group end
|
||||
std::stringstream ss;
|
||||
removed_group.updateConfigObject(is, ss, "}", tab_depth + 1);
|
||||
removed_group.updateConfigObject(is, ss, tab_depth + 1);
|
||||
break;
|
||||
} else {
|
||||
printEntry(os, name, it->second, tab_depth);
|
||||
@ -273,6 +304,9 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
|
||||
}
|
||||
}
|
||||
|
||||
if (!line.empty() && is.eof())
|
||||
os << "\n";
|
||||
|
||||
// Add any settings in the object that don't exist in the config file yet
|
||||
for (it = m_settings.begin(); it != m_settings.end(); ++it) {
|
||||
if (present_entries.find(it->first) != present_entries.end())
|
||||
@ -282,6 +316,12 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
|
||||
was_modified = true;
|
||||
}
|
||||
|
||||
// Append ending tag
|
||||
if (!m_end_tag.empty()) {
|
||||
os << m_end_tag << "\n";
|
||||
was_modified |= !end_found;
|
||||
}
|
||||
|
||||
return was_modified;
|
||||
}
|
||||
|
||||
@ -293,7 +333,7 @@ bool Settings::updateConfigFile(const char *filename)
|
||||
std::ifstream is(filename);
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
|
||||
bool was_modified = updateConfigObject(is, os, "");
|
||||
bool was_modified = updateConfigObject(is, os);
|
||||
is.close();
|
||||
|
||||
if (!was_modified)
|
||||
@ -366,31 +406,39 @@ bool Settings::parseCommandLine(int argc, char *argv[],
|
||||
* Getters *
|
||||
***********/
|
||||
|
||||
Settings *Settings::getParent() const
|
||||
{
|
||||
// If the Settings object is within the hierarchy structure,
|
||||
// iterate towards the origin (0) to find the next fallback layer
|
||||
if (m_settingslayer >= SL_TOTAL_COUNT)
|
||||
return nullptr;
|
||||
|
||||
for (int i = (int)m_settingslayer - 1; i >= 0; --i) {
|
||||
if (s_layers[i])
|
||||
return s_layers[i];
|
||||
}
|
||||
|
||||
// No parent
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const SettingsEntry &Settings::getEntry(const std::string &name) const
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
SettingEntries::const_iterator n;
|
||||
if ((n = m_settings.find(name)) == m_settings.end()) {
|
||||
if ((n = m_defaults.find(name)) == m_defaults.end())
|
||||
throw SettingNotFoundException("Setting [" + name + "] not found.");
|
||||
}
|
||||
return n->second;
|
||||
}
|
||||
|
||||
|
||||
const SettingsEntry &Settings::getEntryDefault(const std::string &name) const
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
SettingEntries::const_iterator n;
|
||||
if ((n = m_defaults.find(name)) == m_defaults.end()) {
|
||||
throw SettingNotFoundException("Setting [" + name + "] not found.");
|
||||
}
|
||||
if ((n = m_settings.find(name)) != m_settings.end())
|
||||
return n->second;
|
||||
}
|
||||
|
||||
if (auto parent = getParent())
|
||||
return parent->getEntry(name);
|
||||
|
||||
throw SettingNotFoundException("Setting [" + name + "] not found.");
|
||||
}
|
||||
|
||||
|
||||
Settings *Settings::getGroup(const std::string &name) const
|
||||
{
|
||||
@ -412,10 +460,15 @@ const std::string &Settings::get(const std::string &name) const
|
||||
|
||||
const std::string &Settings::getDefault(const std::string &name) const
|
||||
{
|
||||
const SettingsEntry &entry = getEntryDefault(name);
|
||||
if (entry.is_group)
|
||||
const SettingsEntry *entry;
|
||||
if (auto parent = getParent())
|
||||
entry = &parent->getEntry(name);
|
||||
else
|
||||
entry = &getEntry(name); // Bottom of the chain
|
||||
|
||||
if (entry->is_group)
|
||||
throw SettingNotFoundException("Setting [" + name + "] is a group.");
|
||||
return entry.value;
|
||||
return entry->value;
|
||||
}
|
||||
|
||||
|
||||
@ -491,18 +544,14 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
|
||||
u32 *flagmask) const
|
||||
{
|
||||
u32 flags = 0;
|
||||
u32 mask_default = 0;
|
||||
|
||||
std::string value;
|
||||
// Read default value (if there is any)
|
||||
if (getDefaultNoEx(name, value)) {
|
||||
flags = std::isdigit(value[0])
|
||||
? stoi(value)
|
||||
: readFlagString(value, flagdesc, &mask_default);
|
||||
}
|
||||
if (auto parent = getParent())
|
||||
flags = parent->getFlagStr(name, flagdesc, flagmask);
|
||||
|
||||
// Apply custom flags "on top"
|
||||
value = get(name);
|
||||
if (m_settings.find(name) != m_settings.end()) {
|
||||
std::string value = get(name);
|
||||
u32 flags_user;
|
||||
u32 mask_user = U32_MAX;
|
||||
flags_user = std::isdigit(value[0])
|
||||
@ -511,9 +560,9 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
|
||||
|
||||
flags &= ~mask_user;
|
||||
flags |= flags_user;
|
||||
|
||||
if (flagmask)
|
||||
*flagmask = mask_default | mask_user;
|
||||
*flagmask |= mask_user;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
@ -521,7 +570,12 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
|
||||
|
||||
bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
|
||||
{
|
||||
return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np);
|
||||
if (getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np))
|
||||
return true;
|
||||
if (auto parent = getParent())
|
||||
return parent->getNoiseParams(name, np);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -583,13 +637,18 @@ bool Settings::exists(const std::string &name) const
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
return (m_settings.find(name) != m_settings.end() ||
|
||||
m_defaults.find(name) != m_defaults.end());
|
||||
if (m_settings.find(name) != m_settings.end())
|
||||
return true;
|
||||
if (auto parent = getParent())
|
||||
return parent->exists(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string> Settings::getNames() const
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (const auto &settings_it : m_settings) {
|
||||
names.push_back(settings_it.first);
|
||||
@ -625,17 +684,6 @@ bool Settings::getNoEx(const std::string &name, std::string &val) const
|
||||
}
|
||||
|
||||
|
||||
bool Settings::getDefaultNoEx(const std::string &name, std::string &val) const
|
||||
{
|
||||
try {
|
||||
val = getDefault(name);
|
||||
return true;
|
||||
} catch (SettingNotFoundException &e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Settings::getFlag(const std::string &name) const
|
||||
{
|
||||
try {
|
||||
@ -746,24 +794,25 @@ bool Settings::getFlagStrNoEx(const std::string &name, u32 &val,
|
||||
***********/
|
||||
|
||||
bool Settings::setEntry(const std::string &name, const void *data,
|
||||
bool set_group, bool set_default)
|
||||
bool set_group)
|
||||
{
|
||||
Settings *old_group = NULL;
|
||||
|
||||
if (!checkNameValid(name))
|
||||
return false;
|
||||
if (!set_group && !checkValueValid(*(const std::string *)data))
|
||||
return false;
|
||||
|
||||
Settings *old_group = NULL;
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
|
||||
SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name];
|
||||
SettingsEntry &entry = m_settings[name];
|
||||
old_group = entry.group;
|
||||
|
||||
entry.value = set_group ? "" : *(const std::string *)data;
|
||||
entry.group = set_group ? *(Settings **)data : NULL;
|
||||
entry.is_group = set_group;
|
||||
if (set_group)
|
||||
entry.group->m_end_tag = "}";
|
||||
}
|
||||
|
||||
delete old_group;
|
||||
@ -774,7 +823,7 @@ bool Settings::setEntry(const std::string &name, const void *data,
|
||||
|
||||
bool Settings::set(const std::string &name, const std::string &value)
|
||||
{
|
||||
if (!setEntry(name, &value, false, false))
|
||||
if (!setEntry(name, &value, false))
|
||||
return false;
|
||||
|
||||
doCallbacks(name);
|
||||
@ -782,9 +831,10 @@ bool Settings::set(const std::string &name, const std::string &value)
|
||||
}
|
||||
|
||||
|
||||
// TODO: Remove this function
|
||||
bool Settings::setDefault(const std::string &name, const std::string &value)
|
||||
{
|
||||
return setEntry(name, &value, false, true);
|
||||
return getLayer(SL_DEFAULTS)->set(name, value);
|
||||
}
|
||||
|
||||
|
||||
@ -794,17 +844,7 @@ bool Settings::setGroup(const std::string &name, const Settings &group)
|
||||
// avoid double-free by copying the source
|
||||
Settings *copy = new Settings();
|
||||
*copy = group;
|
||||
return setEntry(name, ©, true, false);
|
||||
}
|
||||
|
||||
|
||||
bool Settings::setGroupDefault(const std::string &name, const Settings &group)
|
||||
{
|
||||
// Settings must own the group pointer
|
||||
// avoid double-free by copying the source
|
||||
Settings *copy = new Settings();
|
||||
*copy = group;
|
||||
return setEntry(name, ©, true, true);
|
||||
return setEntry(name, ©, true);
|
||||
}
|
||||
|
||||
|
||||
@ -874,8 +914,7 @@ bool Settings::setFlagStr(const std::string &name, u32 flags,
|
||||
}
|
||||
|
||||
|
||||
bool Settings::setNoiseParams(const std::string &name,
|
||||
const NoiseParams &np, bool set_default)
|
||||
bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np)
|
||||
{
|
||||
Settings *group = new Settings;
|
||||
|
||||
@ -888,7 +927,7 @@ bool Settings::setNoiseParams(const std::string &name,
|
||||
group->setFloat("lacunarity", np.lacunarity);
|
||||
group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags);
|
||||
|
||||
return setEntry(name, &group, true, set_default);
|
||||
return setEntry(name, &group, true);
|
||||
}
|
||||
|
||||
|
||||
@ -912,20 +951,8 @@ bool Settings::remove(const std::string &name)
|
||||
}
|
||||
|
||||
|
||||
void Settings::clear()
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
clearNoLock();
|
||||
}
|
||||
|
||||
void Settings::clearDefaults()
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
clearDefaultsNoLock();
|
||||
}
|
||||
|
||||
SettingsParseEvent Settings::parseConfigObject(const std::string &line,
|
||||
const std::string &end, std::string &name, std::string &value)
|
||||
std::string &name, std::string &value)
|
||||
{
|
||||
std::string trimmed_line = trim(line);
|
||||
|
||||
@ -933,7 +960,7 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line,
|
||||
return SPE_NONE;
|
||||
if (trimmed_line[0] == '#')
|
||||
return SPE_COMMENT;
|
||||
if (trimmed_line == end)
|
||||
if (trimmed_line == m_end_tag)
|
||||
return SPE_END;
|
||||
|
||||
size_t pos = trimmed_line.find('=');
|
||||
@ -952,67 +979,26 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line,
|
||||
}
|
||||
|
||||
|
||||
void Settings::updateNoLock(const Settings &other)
|
||||
{
|
||||
m_settings.insert(other.m_settings.begin(), other.m_settings.end());
|
||||
m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
|
||||
}
|
||||
|
||||
|
||||
void Settings::clearNoLock()
|
||||
{
|
||||
|
||||
for (SettingEntries::const_iterator it = m_settings.begin();
|
||||
it != m_settings.end(); ++it)
|
||||
delete it->second.group;
|
||||
m_settings.clear();
|
||||
|
||||
clearDefaultsNoLock();
|
||||
}
|
||||
|
||||
void Settings::clearDefaultsNoLock()
|
||||
{
|
||||
for (SettingEntries::const_iterator it = m_defaults.begin();
|
||||
it != m_defaults.end(); ++it)
|
||||
delete it->second.group;
|
||||
m_defaults.clear();
|
||||
}
|
||||
|
||||
void Settings::setDefault(const std::string &name, const FlagDesc *flagdesc,
|
||||
u32 flags)
|
||||
{
|
||||
m_flags[name] = flagdesc;
|
||||
s_flags[name] = flagdesc;
|
||||
setDefault(name, writeFlagString(flags, flagdesc, U32_MAX));
|
||||
}
|
||||
|
||||
void Settings::overrideDefaults(Settings *other)
|
||||
{
|
||||
for (const auto &setting : other->m_settings) {
|
||||
if (setting.second.is_group) {
|
||||
setGroupDefault(setting.first, *setting.second.group);
|
||||
continue;
|
||||
}
|
||||
const FlagDesc *flagdesc = getFlagDescFallback(setting.first);
|
||||
if (flagdesc) {
|
||||
// Flags cannot be copied directly.
|
||||
// 1) Get the current set flags
|
||||
u32 flags = getFlagStr(setting.first, flagdesc, nullptr);
|
||||
// 2) Set the flags as defaults
|
||||
other->setDefault(setting.first, flagdesc, flags);
|
||||
// 3) Get the newly set flags and override the default setting value
|
||||
setDefault(setting.first, flagdesc,
|
||||
other->getFlagStr(setting.first, flagdesc, nullptr));
|
||||
continue;
|
||||
}
|
||||
// Also covers FlagDesc settings
|
||||
setDefault(setting.first, setting.second.value);
|
||||
}
|
||||
}
|
||||
|
||||
const FlagDesc *Settings::getFlagDescFallback(const std::string &name) const
|
||||
{
|
||||
auto it = m_flags.find(name);
|
||||
return it == m_flags.end() ? nullptr : it->second;
|
||||
auto it = s_flags.find(name);
|
||||
return it == s_flags.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
void Settings::registerChangedCallback(const std::string &name,
|
||||
|
@ -30,7 +30,7 @@ class Settings;
|
||||
struct NoiseParams;
|
||||
|
||||
// Global objects
|
||||
extern Settings *g_settings;
|
||||
extern Settings *g_settings; // Same as Settings::getLayer(SL_GLOBAL);
|
||||
extern std::string g_settings_path;
|
||||
|
||||
// Type for a settings changed callback function
|
||||
@ -60,6 +60,14 @@ enum SettingsParseEvent {
|
||||
SPE_MULTILINE,
|
||||
};
|
||||
|
||||
enum SettingsLayer {
|
||||
SL_DEFAULTS,
|
||||
SL_GAME,
|
||||
SL_GLOBAL,
|
||||
SL_MAP,
|
||||
SL_TOTAL_COUNT
|
||||
};
|
||||
|
||||
struct ValueSpec {
|
||||
ValueSpec(ValueType a_type, const char *a_help=NULL)
|
||||
{
|
||||
@ -92,8 +100,13 @@ typedef std::unordered_map<std::string, SettingsEntry> SettingEntries;
|
||||
|
||||
class Settings {
|
||||
public:
|
||||
Settings() = default;
|
||||
static Settings *createLayer(SettingsLayer sl, const std::string &end_tag = "");
|
||||
static Settings *getLayer(SettingsLayer sl);
|
||||
SettingsLayer getLayerType() const { return m_settingslayer; }
|
||||
|
||||
Settings(const std::string &end_tag = "") :
|
||||
m_end_tag(end_tag)
|
||||
{}
|
||||
~Settings();
|
||||
|
||||
Settings & operator += (const Settings &other);
|
||||
@ -110,7 +123,7 @@ public:
|
||||
// NOTE: Types of allowed_options are ignored. Returns success.
|
||||
bool parseCommandLine(int argc, char *argv[],
|
||||
std::map<std::string, ValueSpec> &allowed_options);
|
||||
bool parseConfigLines(std::istream &is, const std::string &end = "");
|
||||
bool parseConfigLines(std::istream &is);
|
||||
void writeLines(std::ostream &os, u32 tab_depth=0) const;
|
||||
|
||||
/***********
|
||||
@ -146,7 +159,6 @@ public:
|
||||
|
||||
bool getGroupNoEx(const std::string &name, Settings *&val) const;
|
||||
bool getNoEx(const std::string &name, std::string &val) const;
|
||||
bool getDefaultNoEx(const std::string &name, std::string &val) const;
|
||||
bool getFlag(const std::string &name) const;
|
||||
bool getU16NoEx(const std::string &name, u16 &val) const;
|
||||
bool getS16NoEx(const std::string &name, s16 &val) const;
|
||||
@ -170,11 +182,10 @@ public:
|
||||
// N.B. Groups not allocated with new must be set to NULL in the settings
|
||||
// tree before object destruction.
|
||||
bool setEntry(const std::string &name, const void *entry,
|
||||
bool set_group, bool set_default);
|
||||
bool set_group);
|
||||
bool set(const std::string &name, const std::string &value);
|
||||
bool setDefault(const std::string &name, const std::string &value);
|
||||
bool setGroup(const std::string &name, const Settings &group);
|
||||
bool setGroupDefault(const std::string &name, const Settings &group);
|
||||
bool setBool(const std::string &name, bool value);
|
||||
bool setS16(const std::string &name, s16 value);
|
||||
bool setU16(const std::string &name, u16 value);
|
||||
@ -185,21 +196,16 @@ public:
|
||||
bool setV3F(const std::string &name, v3f value);
|
||||
bool setFlagStr(const std::string &name, u32 flags,
|
||||
const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX);
|
||||
bool setNoiseParams(const std::string &name, const NoiseParams &np,
|
||||
bool set_default=false);
|
||||
bool setNoiseParams(const std::string &name, const NoiseParams &np);
|
||||
|
||||
// remove a setting
|
||||
bool remove(const std::string &name);
|
||||
void clear();
|
||||
void clearDefaults();
|
||||
|
||||
/**************
|
||||
* Miscellany *
|
||||
**************/
|
||||
|
||||
void setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags);
|
||||
// Takes the provided setting values and uses them as new defaults
|
||||
void overrideDefaults(Settings *other);
|
||||
const FlagDesc *getFlagDescFallback(const std::string &name) const;
|
||||
|
||||
void registerChangedCallback(const std::string &name,
|
||||
@ -215,9 +221,9 @@ private:
|
||||
***********************/
|
||||
|
||||
SettingsParseEvent parseConfigObject(const std::string &line,
|
||||
const std::string &end, std::string &name, std::string &value);
|
||||
std::string &name, std::string &value);
|
||||
bool updateConfigObject(std::istream &is, std::ostream &os,
|
||||
const std::string &end, u32 tab_depth=0);
|
||||
u32 tab_depth=0);
|
||||
|
||||
static bool checkNameValid(const std::string &name);
|
||||
static bool checkValueValid(const std::string &value);
|
||||
@ -228,9 +234,9 @@ private:
|
||||
/***********
|
||||
* Getters *
|
||||
***********/
|
||||
Settings *getParent() const;
|
||||
|
||||
const SettingsEntry &getEntry(const std::string &name) const;
|
||||
const SettingsEntry &getEntryDefault(const std::string &name) const;
|
||||
|
||||
// Allow TestSettings to run sanity checks using private functions.
|
||||
friend class TestSettings;
|
||||
@ -242,14 +248,15 @@ private:
|
||||
void doCallbacks(const std::string &name) const;
|
||||
|
||||
SettingEntries m_settings;
|
||||
SettingEntries m_defaults;
|
||||
std::unordered_map<std::string, const FlagDesc *> m_flags;
|
||||
|
||||
SettingsCallbackMap m_callbacks;
|
||||
std::string m_end_tag;
|
||||
|
||||
mutable std::mutex m_callback_mutex;
|
||||
|
||||
// All methods that access m_settings/m_defaults directly should lock this.
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
static Settings *s_layers[SL_TOTAL_COUNT];
|
||||
SettingsLayer m_settingslayer = SL_TOTAL_COUNT;
|
||||
static std::unordered_map<std::string, const FlagDesc *> s_flags;
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
TestMapSettingsManager() { TestManager::registerTestModule(this); }
|
||||
const char *getName() { return "TestMapSettingsManager"; }
|
||||
|
||||
void makeUserConfig(Settings *conf);
|
||||
void makeUserConfig();
|
||||
std::string makeMetaFile(bool make_corrupt);
|
||||
|
||||
void runTests(IGameDef *gamedef);
|
||||
@ -65,8 +65,11 @@ void check_noise_params(const NoiseParams *np1, const NoiseParams *np2)
|
||||
}
|
||||
|
||||
|
||||
void TestMapSettingsManager::makeUserConfig(Settings *conf)
|
||||
void TestMapSettingsManager::makeUserConfig()
|
||||
{
|
||||
delete Settings::getLayer(SL_GLOBAL);
|
||||
Settings *conf = Settings::createLayer(SL_GLOBAL);
|
||||
|
||||
conf->set("mg_name", "v7");
|
||||
conf->set("seed", "5678");
|
||||
conf->set("water_level", "20");
|
||||
@ -103,12 +106,11 @@ std::string TestMapSettingsManager::makeMetaFile(bool make_corrupt)
|
||||
|
||||
void TestMapSettingsManager::testMapSettingsManager()
|
||||
{
|
||||
Settings user_settings;
|
||||
makeUserConfig(&user_settings);
|
||||
makeUserConfig();
|
||||
|
||||
std::string test_mapmeta_path = makeMetaFile(false);
|
||||
|
||||
MapSettingsManager mgr(&user_settings, test_mapmeta_path);
|
||||
MapSettingsManager mgr(test_mapmeta_path);
|
||||
std::string value;
|
||||
|
||||
UASSERT(mgr.getMapSetting("mg_name", &value));
|
||||
@ -140,6 +142,12 @@ void TestMapSettingsManager::testMapSettingsManager()
|
||||
mgr.setMapSettingNoiseParams("mgv5_np_height", &script_np_height);
|
||||
mgr.setMapSettingNoiseParams("mgv5_np_factor", &script_np_factor);
|
||||
|
||||
{
|
||||
NoiseParams dummy;
|
||||
mgr.getMapSettingNoiseParams("mgv5_np_factor", &dummy);
|
||||
check_noise_params(&dummy, &script_np_factor);
|
||||
}
|
||||
|
||||
// Now make our Params and see if the values are correctly sourced
|
||||
MapgenParams *params = mgr.makeMapgenParams();
|
||||
UASSERT(params->mgtype == MAPGEN_V5);
|
||||
@ -188,50 +196,66 @@ void TestMapSettingsManager::testMapSettingsManager()
|
||||
|
||||
void TestMapSettingsManager::testMapMetaSaveLoad()
|
||||
{
|
||||
Settings conf;
|
||||
std::string path = getTestTempDirectory()
|
||||
+ DIR_DELIM + "foobar" + DIR_DELIM + "map_meta.txt";
|
||||
|
||||
makeUserConfig();
|
||||
Settings &conf = *Settings::getLayer(SL_GLOBAL);
|
||||
|
||||
// There cannot be two MapSettingsManager
|
||||
// copy the mapgen params to compare them
|
||||
MapgenParams params1, params2;
|
||||
// Create a set of mapgen params and save them to map meta
|
||||
{
|
||||
conf.set("seed", "12345");
|
||||
conf.set("water_level", "5");
|
||||
MapSettingsManager mgr1(&conf, path);
|
||||
MapgenParams *params1 = mgr1.makeMapgenParams();
|
||||
UASSERT(params1);
|
||||
UASSERT(mgr1.saveMapMeta());
|
||||
MapSettingsManager mgr(path);
|
||||
MapgenParams *params = mgr.makeMapgenParams();
|
||||
UASSERT(params);
|
||||
params1 = *params;
|
||||
params1.bparams = nullptr; // No double-free
|
||||
UASSERT(mgr.saveMapMeta());
|
||||
}
|
||||
|
||||
// Now try loading the map meta to mapgen params
|
||||
{
|
||||
conf.set("seed", "67890");
|
||||
conf.set("water_level", "32");
|
||||
MapSettingsManager mgr2(&conf, path);
|
||||
UASSERT(mgr2.loadMapMeta());
|
||||
MapgenParams *params2 = mgr2.makeMapgenParams();
|
||||
UASSERT(params2);
|
||||
MapSettingsManager mgr(path);
|
||||
UASSERT(mgr.loadMapMeta());
|
||||
MapgenParams *params = mgr.makeMapgenParams();
|
||||
UASSERT(params);
|
||||
params2 = *params;
|
||||
params2.bparams = nullptr; // No double-free
|
||||
}
|
||||
|
||||
// Check that both results are correct
|
||||
UASSERTEQ(u64, params1->seed, 12345);
|
||||
UASSERTEQ(s16, params1->water_level, 5);
|
||||
UASSERTEQ(u64, params2->seed, 12345);
|
||||
UASSERTEQ(s16, params2->water_level, 5);
|
||||
UASSERTEQ(u64, params1.seed, 12345);
|
||||
UASSERTEQ(s16, params1.water_level, 5);
|
||||
UASSERTEQ(u64, params2.seed, 12345);
|
||||
UASSERTEQ(s16, params2.water_level, 5);
|
||||
}
|
||||
|
||||
|
||||
void TestMapSettingsManager::testMapMetaFailures()
|
||||
{
|
||||
std::string test_mapmeta_path;
|
||||
Settings conf;
|
||||
|
||||
// Check to see if it'll fail on a non-existent map meta file
|
||||
{
|
||||
test_mapmeta_path = "woobawooba/fgdfg/map_meta.txt";
|
||||
UASSERT(!fs::PathExists(test_mapmeta_path));
|
||||
|
||||
MapSettingsManager mgr1(&conf, test_mapmeta_path);
|
||||
MapSettingsManager mgr1(test_mapmeta_path);
|
||||
UASSERT(!mgr1.loadMapMeta());
|
||||
}
|
||||
|
||||
// Check to see if it'll fail on a corrupt map meta file
|
||||
{
|
||||
test_mapmeta_path = makeMetaFile(true);
|
||||
UASSERT(fs::PathExists(test_mapmeta_path));
|
||||
|
||||
MapSettingsManager mgr2(&conf, test_mapmeta_path);
|
||||
MapSettingsManager mgr2(test_mapmeta_path);
|
||||
UASSERT(!mgr2.loadMapMeta());
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#include <cmath>
|
||||
#include "settings.h"
|
||||
#include "defaultsettings.h"
|
||||
#include "noise.h"
|
||||
|
||||
class TestSettings : public TestBase {
|
||||
@ -31,6 +32,7 @@ public:
|
||||
void runTests(IGameDef *gamedef);
|
||||
|
||||
void testAllSettings();
|
||||
void testDefaults();
|
||||
void testFlagDesc();
|
||||
|
||||
static const char *config_text_before;
|
||||
@ -42,6 +44,7 @@ static TestSettings g_test_instance;
|
||||
void TestSettings::runTests(IGameDef *gamedef)
|
||||
{
|
||||
TEST(testAllSettings);
|
||||
TEST(testDefaults);
|
||||
TEST(testFlagDesc);
|
||||
}
|
||||
|
||||
@ -70,7 +73,8 @@ const char *TestSettings::config_text_before =
|
||||
" with leading whitespace!\n"
|
||||
"\"\"\"\n"
|
||||
"np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\n"
|
||||
"zoop = true";
|
||||
"zoop = true\n"
|
||||
"[dummy_eof_end_tag]\n";
|
||||
|
||||
const std::string TestSettings::config_text_after =
|
||||
"leet = 1337\n"
|
||||
@ -111,12 +115,34 @@ const std::string TestSettings::config_text_after =
|
||||
" animals = cute\n"
|
||||
" num_apples = 4\n"
|
||||
" num_oranges = 53\n"
|
||||
"}\n";
|
||||
"}\n"
|
||||
"[dummy_eof_end_tag]";
|
||||
|
||||
void compare_settings(const std::string &name, Settings *a, Settings *b)
|
||||
{
|
||||
auto keys = a->getNames();
|
||||
Settings *group1, *group2;
|
||||
std::string value1, value2;
|
||||
for (auto &key : keys) {
|
||||
if (a->getGroupNoEx(key, group1)) {
|
||||
UASSERT(b->getGroupNoEx(key, group2));
|
||||
|
||||
compare_settings(name + "->" + key, group1, group2);
|
||||
continue;
|
||||
}
|
||||
|
||||
UASSERT(b->getNoEx(key, value1));
|
||||
// For identification
|
||||
value1 = name + "->" + key + "=" + value1;
|
||||
value2 = name + "->" + key + "=" + a->get(key);
|
||||
UASSERTCMP(std::string, ==, value2, value1);
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettings::testAllSettings()
|
||||
{
|
||||
try {
|
||||
Settings s;
|
||||
Settings s("[dummy_eof_end_tag]");
|
||||
|
||||
// Test reading of settings
|
||||
std::istringstream is(config_text_before);
|
||||
@ -197,21 +223,44 @@ void TestSettings::testAllSettings()
|
||||
is.clear();
|
||||
is.seekg(0);
|
||||
|
||||
UASSERT(s.updateConfigObject(is, os, "", 0) == true);
|
||||
//printf(">>>> expected config:\n%s\n", TEST_CONFIG_TEXT_AFTER);
|
||||
//printf(">>>> actual config:\n%s\n", os.str().c_str());
|
||||
#if __cplusplus < 201103L
|
||||
// This test only works in older C++ versions than C++11 because we use unordered_map
|
||||
UASSERT(os.str() == config_text_after);
|
||||
#endif
|
||||
UASSERT(s.updateConfigObject(is, os, 0) == true);
|
||||
|
||||
{
|
||||
// Confirm settings
|
||||
Settings s2("[dummy_eof_end_tag]");
|
||||
std::istringstream is(config_text_after, std::ios_base::binary);
|
||||
s2.parseConfigLines(is);
|
||||
|
||||
compare_settings("(main)", &s, &s2);
|
||||
}
|
||||
|
||||
} catch (SettingNotFoundException &e) {
|
||||
UASSERT(!"Setting not found!");
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettings::testDefaults()
|
||||
{
|
||||
Settings *game = Settings::createLayer(SL_GAME);
|
||||
Settings *def = Settings::getLayer(SL_DEFAULTS);
|
||||
|
||||
def->set("name", "FooBar");
|
||||
UASSERT(def->get("name") == "FooBar");
|
||||
UASSERT(game->get("name") == "FooBar");
|
||||
|
||||
game->set("name", "Baz");
|
||||
UASSERT(game->get("name") == "Baz");
|
||||
|
||||
delete game;
|
||||
|
||||
// Restore default settings
|
||||
delete Settings::getLayer(SL_DEFAULTS);
|
||||
set_default_settings();
|
||||
}
|
||||
|
||||
void TestSettings::testFlagDesc()
|
||||
{
|
||||
Settings s;
|
||||
Settings &s = *Settings::createLayer(SL_GAME);
|
||||
FlagDesc flagdesc[] = {
|
||||
{ "biomes", 0x01 },
|
||||
{ "trees", 0x02 },
|
||||
@ -242,4 +291,6 @@ void TestSettings::testFlagDesc()
|
||||
// Enabled: tables
|
||||
s.set("test_flags", "16");
|
||||
UASSERT(s.getFlagStr("test_flags", flagdesc, nullptr) == 0x10);
|
||||
|
||||
delete &s;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user