summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSmallJoker <mk939@ymail.com>2020-11-22 17:49:30 +0100
committersfan5 <sfan5@live.de>2021-01-29 17:32:35 +0100
commit37a05ec8d6cbf9ff4432225cffe78c16fdd0647d (patch)
tree727e4272f1f16fe1a6a51484d1bba728c8632615
parent5e9dd1667b244df4e7767be404d4a12966d6a90a (diff)
downloadminetest-37a05ec8d6cbf9ff4432225cffe78c16fdd0647d.tar.gz
minetest-37a05ec8d6cbf9ff4432225cffe78c16fdd0647d.tar.bz2
minetest-37a05ec8d6cbf9ff4432225cffe78c16fdd0647d.zip
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
-rw-r--r--src/content/subgames.cpp24
-rw-r--r--src/database/database-files.cpp15
-rw-r--r--src/database/database-files.h4
-rw-r--r--src/defaultsettings.cpp4
-rw-r--r--src/defaultsettings.h9
-rw-r--r--src/gui/guiKeyChangeMenu.cpp2
-rw-r--r--src/main.cpp6
-rw-r--r--src/map.cpp2
-rw-r--r--src/map_settings_manager.cpp67
-rw-r--r--src/map_settings_manager.h5
-rw-r--r--src/remoteplayer.h1
-rw-r--r--src/script/lua_api/l_mapgen.cpp2
-rw-r--r--src/script/lua_api/l_settings.cpp2
-rw-r--r--src/script/scripting_mainmenu.cpp2
-rw-r--r--src/server.cpp3
-rw-r--r--src/server.h1
-rw-r--r--src/serverenvironment.cpp7
-rw-r--r--src/settings.cpp300
-rw-r--r--src/settings.h43
-rw-r--r--src/unittest/test_map_settings_manager.cpp86
-rw-r--r--src/unittest/test_settings.cpp73
21 files changed, 359 insertions, 299 deletions
diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp
index c6350f2dd..e9dc609b0 100644
--- a/src/content/subgames.cpp
+++ b/src/content/subgames.cpp
@@ -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_defaults;
- getGameMinetestConfig(gamespec.path, game_defaults);
- game_defaults.removeSecureSettings();
+ 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);
+ }
- 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;
}
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
index 529fb8763..d9e8f24ea 100644
--- a/src/database/database-files.cpp
+++ b/src/database/database-files.cpp
@@ -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();
diff --git a/src/database/database-files.h b/src/database/database-files.h
index a041cb1ff..e647a2e24 100644
--- a/src/database/database-files.h
+++ b/src/database/database-files.h
@@ -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
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 114351d86..d34ec324b 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -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", "");
diff --git a/src/defaultsettings.h b/src/defaultsettings.h
index c81e88502..c239b3c12 100644
--- a/src/defaultsettings.h
+++ b/src/defaultsettings.h
@@ -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();
diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp
index eb641d952..4dcb47779 100644
--- a/src/gui/guiKeyChangeMenu.cpp
+++ b/src/gui/guiKeyChangeMenu.cpp
@@ -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());
diff --git a/src/main.cpp b/src/main.cpp
index f7238176b..57768dbb2 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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) {
diff --git a/src/map.cpp b/src/map.cpp
index aff545921..7c59edbaa 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -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;
diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp
index 9c447b3d0..ed65eed1c 100644
--- a/src/map_settings_manager.cpp
+++ b/src/map_settings_manager.cpp
@@ -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 " <<
diff --git a/src/map_settings_manager.h b/src/map_settings_manager.h
index 5baa38455..9258d3032 100644
--- a/src/map_settings_manager.h
+++ b/src/map_settings_manager.h
@@ -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;
};
diff --git a/src/remoteplayer.h b/src/remoteplayer.h
index b82cbc08e..8d086fc5a 100644
--- a/src/remoteplayer.h
+++ b/src/remoteplayer.h
@@ -140,7 +140,6 @@ public:
void onSuccessfulSave();
private:
-
PlayerSAO *m_sao = nullptr;
bool m_dirty = false;
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index fad08e1f6..183f20540 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -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;
}
diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp
index 33eb02392..bcbaf15fa 100644
--- a/src/script/lua_api/l_settings.cpp
+++ b/src/script/lua_api/l_settings.cpp
@@ -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;
}
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 0f672f917..9b377366e 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -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
diff --git a/src/server.cpp b/src/server.cpp
index b5352749c..aba7b6401 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -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,
diff --git a/src/server.h b/src/server.h
index a7e85d0e1..1dd181794 100644
--- a/src/server.h
+++ b/src/server.h
@@ -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;
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index 56dbb0632..3d9ba132b 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -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!");
}
diff --git a/src/settings.cpp b/src/settings.cpp
index f30ef34e9..cf2a16aa6 100644
--- a/src/settings.cpp
+++ b/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,29 +406,37 @@ bool Settings::parseCommandLine(int argc, char *argv[],
* Getters *
***********/
-
-const SettingsEntry &Settings::getEntry(const std::string &name) const
+Settings *Settings::getParent() const
{
- MutexAutoLock lock(m_mutex);
+ // 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;
- 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.");
+ for (int i = (int)m_settingslayer - 1; i >= 0; --i) {
+ if (s_layers[i])
+ return s_layers[i];
}
- return n->second;
+
+ // No parent
+ return nullptr;
}
-const SettingsEntry &Settings::getEntryDefault(const std::string &name) const
+const SettingsEntry &Settings::getEntry(const std::string &name) const
{
- MutexAutoLock lock(m_mutex);
+ {
+ MutexAutoLock lock(m_mutex);
- SettingEntries::const_iterator n;
- if ((n = m_defaults.find(name)) == m_defaults.end()) {
- throw SettingNotFoundException("Setting [" + name + "] not found.");
+ SettingEntries::const_iterator n;
+ if ((n = m_settings.find(name)) != m_settings.end())
+ return n->second;
}
- return n->second;
+
+ if (auto parent = getParent())
+ return parent->getEntry(name);
+
+ throw SettingNotFoundException("Setting [" + name + "] not found.");
}
@@ -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,29 +544,25 @@ 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);
- u32 flags_user;
- u32 mask_user = U32_MAX;
- flags_user = std::isdigit(value[0])
- ? stoi(value) // Override default
- : readFlagString(value, flagdesc, &mask_user);
-
- flags &= ~mask_user;
- flags |= flags_user;
-
- if (flagmask)
- *flagmask = mask_default | mask_user;
+ 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])
+ ? stoi(value) // Override default
+ : readFlagString(value, flagdesc, &mask_user);
+
+ flags &= ~mask_user;
+ flags |= flags_user;
+ if (flagmask)
+ *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, &copy, 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, &copy, true, true);
+ return setEntry(name, &copy, 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,
diff --git a/src/settings.h b/src/settings.h
index 6db2f9481..af4cae3cd 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -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;
};
diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp
index 3d642a9b4..81ca68705 100644
--- a/src/unittest/test_map_settings_manager.cpp
+++ b/src/unittest/test_map_settings_manager.cpp
@@ -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());
+ {
+ conf.set("seed", "12345");
+ conf.set("water_level", "5");
+ 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);
+ {
+ conf.set("seed", "67890");
+ conf.set("water_level", "32");
+ 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));
+ {
+ test_mapmeta_path = "woobawooba/fgdfg/map_meta.txt";
+ UASSERT(!fs::PathExists(test_mapmeta_path));
- MapSettingsManager mgr1(&conf, test_mapmeta_path);
- UASSERT(!mgr1.loadMapMeta());
+ 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));
+ {
+ test_mapmeta_path = makeMetaFile(true);
+ UASSERT(fs::PathExists(test_mapmeta_path));
- MapSettingsManager mgr2(&conf, test_mapmeta_path);
- UASSERT(!mgr2.loadMapMeta());
+ MapSettingsManager mgr2(test_mapmeta_path);
+ UASSERT(!mgr2.loadMapMeta());
+ }
}
diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp
index f91ba5b67..d2d35c357 100644
--- a/src/unittest/test_settings.cpp
+++ b/src/unittest/test_settings.cpp
@@ -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;
}