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:
SmallJoker 2020-11-22 17:49:30 +01:00 committed by sfan5
parent 5e9dd1667b
commit 37a05ec8d6
21 changed files with 358 additions and 298 deletions

View File

@ -329,18 +329,16 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
} }
} }
// Override defaults with those provided by the game. Settings *game_settings = Settings::getLayer(SL_GAME);
// We clear and reload the defaults because the defaults const bool new_game_settings = (game_settings == nullptr);
// might have been overridden by other subgame config if (new_game_settings) {
// files that were loaded before. // Called by main-menu without a Server instance running
g_settings->clearDefaults(); // -> create and free manually
set_default_settings(g_settings); game_settings = Settings::createLayer(SL_GAME);
}
Settings game_defaults; getGameMinetestConfig(gamespec.path, *game_settings);
getGameMinetestConfig(gamespec.path, game_defaults); game_settings->removeSecureSettings();
game_defaults.removeSecureSettings();
g_settings->overrideDefaults(&game_defaults);
infostream << "Initializing world at " << final_path << std::endl; 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()); fs::safeWriteToFile(map_meta_path, oss.str());
} }
// The Settings object is no longer needed for created worlds
if (new_game_settings)
delete game_settings;
} }

View File

@ -122,18 +122,17 @@ void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
args.set("name", p->m_name); args.set("name", p->m_name);
// This should not happen // This should not happen
assert(m_sao); PlayerSAO *sao = p->getPlayerSAO();
args.setU16("hp", p->m_sao->getHP()); assert(sao);
args.setV3F("position", p->m_sao->getBasePosition()); args.setU16("hp", sao->getHP());
args.setFloat("pitch", p->m_sao->getLookPitch()); args.setV3F("position", sao->getBasePosition());
args.setFloat("yaw", p->m_sao->getRotation().Y); args.setFloat("pitch", sao->getLookPitch());
args.setU16("breath", p->m_sao->getBreath()); args.setFloat("yaw", sao->getRotation().Y);
args.setU16("breath", sao->getBreath());
std::string extended_attrs; std::string extended_attrs;
{ {
// serializeExtraAttributes // serializeExtraAttributes
PlayerSAO *sao = p->getPlayerSAO();
assert(sao);
Json::Value json_root; Json::Value json_root;
const StringMap &attrs = sao->getMeta().getStrings(); const StringMap &attrs = sao->getMeta().getStrings();

View File

@ -38,8 +38,8 @@ public:
void listPlayers(std::vector<std::string> &res); void listPlayers(std::vector<std::string> &res);
private: private:
void deSerialize(RemotePlayer *p, std::istream &is, void deSerialize(RemotePlayer *p, std::istream &is, const std::string &playername,
const std::string &playername, PlayerSAO *sao); PlayerSAO *sao);
/* /*
serialize() writes a bunch of text that can contain serialize() writes a bunch of text that can contain
any characters except a '\0', and such an ending that any characters except a '\0', and such an ending that

View File

@ -27,8 +27,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen/mapgen.h" // Mapgen::setDefaultSettings #include "mapgen/mapgen.h" // Mapgen::setDefaultSettings
#include "util/string.h" #include "util/string.h"
void set_default_settings(Settings *settings) void set_default_settings()
{ {
Settings *settings = Settings::createLayer(SL_DEFAULTS);
// Client and server // Client and server
settings->setDefault("language", ""); settings->setDefault("language", "");
settings->setDefault("name", ""); settings->setDefault("name", "");

View File

@ -25,11 +25,4 @@ class Settings;
* initialize basic default settings * initialize basic default settings
* @param settings pointer to settings * @param settings pointer to settings
*/ */
void set_default_settings(Settings *settings); void set_default_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);

View File

@ -248,7 +248,7 @@ bool GUIKeyChangeMenu::acceptInput()
{ {
for (key_setting *k : key_settings) { for (key_setting *k : key_settings) {
std::string default_key; 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) if (k->key.sym() != default_key)
g_settings->set(k->setting_name, k->key.sym()); g_settings->set(k->setting_name, k->key.sym());

View File

@ -487,12 +487,15 @@ static bool create_userdata_path()
static bool init_common(const Settings &cmd_args, int argc, char *argv[]) static bool init_common(const Settings &cmd_args, int argc, char *argv[])
{ {
startup_message(); startup_message();
set_default_settings(g_settings); set_default_settings();
// Initialize sockets // Initialize sockets
sockets_init(); sockets_init();
atexit(sockets_cleanup); atexit(sockets_cleanup);
// Initialize g_settings
Settings::createLayer(SL_GLOBAL);
if (!read_config_file(cmd_args)) if (!read_config_file(cmd_args))
return false; return false;
@ -524,6 +527,7 @@ static bool read_config_file(const Settings &cmd_args)
// Path of configuration file in use // Path of configuration file in use
sanity_check(g_settings_path == ""); // Sanity check sanity_check(g_settings_path == ""); // Sanity check
if (cmd_args.exists("config")) { if (cmd_args.exists("config")) {
bool r = g_settings->readConfigFile(cmd_args.get("config").c_str()); bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
if (!r) { if (!r) {

View File

@ -1184,7 +1184,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
EmergeManager *emerge, MetricsBackend *mb): EmergeManager *emerge, MetricsBackend *mb):
Map(gamedef), Map(gamedef),
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
m_emerge(emerge) m_emerge(emerge)
{ {
verbosestream<<FUNCTION_NAME<<std::endl; verbosestream<<FUNCTION_NAME<<std::endl;

View File

@ -25,17 +25,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map_settings_manager.h" #include "map_settings_manager.h"
MapSettingsManager::MapSettingsManager(Settings *user_settings, MapSettingsManager::MapSettingsManager(const std::string &map_meta_path):
const std::string &map_meta_path): m_map_meta_path(map_meta_path)
m_map_meta_path(map_meta_path),
m_map_settings(new Settings()),
m_user_settings(user_settings)
{ {
assert(m_user_settings != NULL); m_map_settings = Settings::createLayer(SL_MAP, "[end_of_params]");
Mapgen::setDefaultSettings(Settings::getLayer(SL_DEFAULTS));
Mapgen::setDefaultSettings(m_map_settings);
// This inherits the combined defaults provided by loadGameConfAndInitWorld.
m_map_settings->overrideDefaults(user_settings);
} }
@ -49,22 +43,23 @@ MapSettingsManager::~MapSettingsManager()
bool MapSettingsManager::getMapSetting( bool MapSettingsManager::getMapSetting(
const std::string &name, std::string *value_out) 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)) if (m_map_settings->getNoEx(name, *value_out))
return true; return true;
// Compatibility kludge // Compatibility kludge
if (m_user_settings == g_settings && name == "seed") if (name == "seed")
return m_user_settings->getNoEx("fixed_map_seed", *value_out); return Settings::getLayer(SL_GLOBAL)->getNoEx("fixed_map_seed", *value_out);
return m_user_settings->getNoEx(name, *value_out); return false;
} }
bool MapSettingsManager::getMapSettingNoiseParams( bool MapSettingsManager::getMapSettingNoiseParams(
const std::string &name, NoiseParams *value_out) const std::string &name, NoiseParams *value_out)
{ {
return m_map_settings->getNoiseParams(name, *value_out) || // TODO: Rename to "getNoiseParams"
m_user_settings->getNoiseParams(name, *value_out); return m_map_settings->getNoiseParams(name, *value_out);
} }
@ -77,7 +72,7 @@ bool MapSettingsManager::setMapSetting(
if (override_meta) if (override_meta)
m_map_settings->set(name, value); m_map_settings->set(name, value);
else else
m_map_settings->setDefault(name, value); Settings::getLayer(SL_GLOBAL)->set(name, value);
return true; return true;
} }
@ -89,7 +84,11 @@ bool MapSettingsManager::setMapSettingNoiseParams(
if (mapgen_params) if (mapgen_params)
return false; 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; return true;
} }
@ -104,8 +103,8 @@ bool MapSettingsManager::loadMapMeta()
return false; return false;
} }
if (!m_map_settings->parseConfigLines(is, "[end_of_params]")) { if (!m_map_settings->parseConfigLines(is)) {
errorstream << "loadMapMeta: [end_of_params] not found!" << std::endl; errorstream << "loadMapMeta: Format error. '[end_of_params]' missing?" << std::endl;
return false; return false;
} }
@ -116,28 +115,22 @@ bool MapSettingsManager::loadMapMeta()
bool MapSettingsManager::saveMapMeta() bool MapSettingsManager::saveMapMeta()
{ {
// If mapgen params haven't been created yet; abort // 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; return false;
}
// Paths set up by subgames.cpp, but not in unittests
if (!fs::CreateAllDirs(fs::RemoveLastPathComponent(m_map_meta_path))) { if (!fs::CreateAllDirs(fs::RemoveLastPathComponent(m_map_meta_path))) {
errorstream << "saveMapMeta: could not create dirs to " errorstream << "saveMapMeta: could not create dirs to "
<< m_map_meta_path; << m_map_meta_path;
return false; return false;
} }
std::ostringstream oss(std::ios_base::binary); mapgen_params->MapgenParams::writeParams(m_map_settings);
Settings conf; mapgen_params->writeParams(m_map_settings);
mapgen_params->MapgenParams::writeParams(&conf); if (!m_map_settings->updateConfigFile(m_map_meta_path.c_str())) {
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())) {
errorstream << "saveMapMeta: could not write " errorstream << "saveMapMeta: could not write "
<< m_map_meta_path << std::endl; << m_map_meta_path << std::endl;
return false; return false;
@ -152,23 +145,21 @@ MapgenParams *MapSettingsManager::makeMapgenParams()
if (mapgen_params) if (mapgen_params)
return mapgen_params; return mapgen_params;
assert(m_user_settings != NULL);
assert(m_map_settings != NULL); assert(m_map_settings != NULL);
// At this point, we have (in order of precedence): // 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 // explicit overrides from scripts
// 2). m_mapgen_settings->m_defaults containing script-set mgparams without // 2). SL_GLOBAL containing all user-specified config file
// overrides
// 3). g_settings->m_settings containing all user-specified config file
// settings // 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) // scripts, e.g. mods using Lua as an enhanced config file)
// Now, get the mapgen type so we can create the appropriate MapgenParams // Now, get the mapgen type so we can create the appropriate MapgenParams
std::string mg_name; std::string mg_name;
MapgenType mgtype = getMapSetting("mg_name", &mg_name) ? MapgenType mgtype = getMapSetting("mg_name", &mg_name) ?
Mapgen::getMapgenType(mg_name) : MAPGEN_DEFAULT; Mapgen::getMapgenType(mg_name) : MAPGEN_DEFAULT;
if (mgtype == MAPGEN_INVALID) { if (mgtype == MAPGEN_INVALID) {
errorstream << "EmergeManager: mapgen '" << mg_name << errorstream << "EmergeManager: mapgen '" << mg_name <<
"' not valid; falling back to " << "' not valid; falling back to " <<

View File

@ -44,8 +44,7 @@ struct MapgenParams;
*/ */
class MapSettingsManager { class MapSettingsManager {
public: public:
MapSettingsManager(Settings *user_settings, MapSettingsManager(const std::string &map_meta_path);
const std::string &map_meta_path);
~MapSettingsManager(); ~MapSettingsManager();
// Finalized map generation parameters // Finalized map generation parameters
@ -71,6 +70,6 @@ public:
private: private:
std::string m_map_meta_path; std::string m_map_meta_path;
// TODO: Rename to "m_settings"
Settings *m_map_settings; Settings *m_map_settings;
Settings *m_user_settings;
}; };

View File

@ -140,7 +140,6 @@ public:
void onSuccessfulSave(); void onSuccessfulSave();
private: private:
PlayerSAO *m_sao = nullptr; PlayerSAO *m_sao = nullptr;
bool m_dirty = false; bool m_dirty = false;

View File

@ -982,7 +982,7 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L)
bool set_default = !lua_isboolean(L, 3) || readParam<bool>(L, 3); 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; return 0;
} }

View File

@ -197,7 +197,7 @@ int LuaSettings::l_set_np_group(lua_State *L)
SET_SECURITY_CHECK(L, key); SET_SECURITY_CHECK(L, key);
o->m_settings->setNoiseParams(key, value, false); o->m_settings->setNoiseParams(key, value);
return 0; return 0;
} }

View File

@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
extern "C" { extern "C" {
#include "lualib.h" #include "lualib.h"
} }
#include "settings.h"
#define MAINMENU_NUM_ASYNC_THREADS 4 #define MAINMENU_NUM_ASYNC_THREADS 4

View File

@ -351,6 +351,7 @@ Server::~Server()
// Deinitialize scripting // Deinitialize scripting
infostream << "Server: Deinitializing scripting" << std::endl; infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script; delete m_script;
delete m_game_settings;
while (!m_unsent_map_edit_queue.empty()) { while (!m_unsent_map_edit_queue.empty()) {
delete m_unsent_map_edit_queue.front(); delete m_unsent_map_edit_queue.front();
@ -368,6 +369,8 @@ void Server::init()
infostream << "- world: " << m_path_world << std::endl; infostream << "- world: " << m_path_world << std::endl;
infostream << "- game: " << m_gamespec.path << std::endl; infostream << "- game: " << m_gamespec.path << std::endl;
m_game_settings = Settings::createLayer(SL_GAME);
// Create world if it doesn't exist // Create world if it doesn't exist
try { try {
loadGameConfAndInitWorld(m_path_world, loadGameConfAndInitWorld(m_path_world,

View File

@ -524,6 +524,7 @@ private:
u16 m_max_chatmessage_length; u16 m_max_chatmessage_length;
// For "dedicated" server list flag // For "dedicated" server list flag
bool m_dedicated; bool m_dedicated;
Settings *m_game_settings = nullptr;
// Thread can set; step() will throw as ServerError // Thread can set; step() will throw as ServerError
MutexedVariable<std::string> m_async_fatal_error; MutexedVariable<std::string> m_async_fatal_error;

View File

@ -632,7 +632,7 @@ void ServerEnvironment::saveMeta()
// Open file and serialize // Open file and serialize
std::ostringstream ss(std::ios_base::binary); std::ostringstream ss(std::ios_base::binary);
Settings args; Settings args("EnvArgsEnd");
args.setU64("game_time", m_game_time); args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay()); args.setU64("time_of_day", getTimeOfDay());
args.setU64("last_clear_objects_time", m_last_clear_objects_time); args.setU64("last_clear_objects_time", m_last_clear_objects_time);
@ -641,7 +641,6 @@ void ServerEnvironment::saveMeta()
m_lbm_mgr.createIntroductionTimesString()); m_lbm_mgr.createIntroductionTimesString());
args.setU64("day_count", m_day_count); args.setU64("day_count", m_day_count);
args.writeLines(ss); args.writeLines(ss);
ss<<"EnvArgsEnd\n";
if(!fs::safeWriteToFile(path, ss.str())) if(!fs::safeWriteToFile(path, ss.str()))
{ {
@ -676,9 +675,9 @@ void ServerEnvironment::loadMeta()
throw SerializationError("Couldn't load env meta"); 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(): " throw SerializationError("ServerEnvironment::loadMeta(): "
"EnvArgsEnd not found!"); "EnvArgsEnd not found!");
} }

View File

@ -33,27 +33,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cctype> #include <cctype>
#include <algorithm> #include <algorithm>
static Settings main_settings; Settings *g_settings = nullptr;
Settings *g_settings = &main_settings;
std::string g_settings_path; 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) sanity_check((int)sl >= 0 && sl < SL_TOTAL_COUNT);
return *this; return s_layers[(size_t)sl];
}
Settings::~Settings()
{
MutexAutoLock lock(m_mutex); 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) if (&other == this)
return *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 lock(m_mutex);
MutexAutoLock lock2(other.m_mutex); MutexAutoLock lock2(other.m_mutex);
clearNoLock(); clearNoLock();
updateNoLock(other); m_settings = other.m_settings;
m_callbacks = other.m_callbacks;
return *this; return *this;
} }
@ -130,11 +157,11 @@ bool Settings::readConfigFile(const char *filename)
if (!is.good()) if (!is.good())
return false; 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); MutexAutoLock lock(m_mutex);
@ -142,7 +169,7 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
while (is.good()) { while (is.good()) {
std::getline(is, line); std::getline(is, line);
SettingsParseEvent event = parseConfigObject(line, end, name, value); SettingsParseEvent event = parseConfigObject(line, name, value);
switch (event) { switch (event) {
case SPE_NONE: case SPE_NONE:
@ -155,8 +182,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
case SPE_END: case SPE_END:
return true; return true;
case SPE_GROUP: { case SPE_GROUP: {
Settings *group = new Settings; Settings *group = new Settings("}");
if (!group->parseConfigLines(is, "}")) { if (!group->parseConfigLines(is)) {
delete group; delete group;
return false; 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) for (const auto &setting_it : m_settings)
printEntry(os, setting_it.first, setting_it.second, tab_depth); 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); entry.group->writeLines(os, tab_depth + 1);
for (u32 i = 0; i != tab_depth; i++) // Closing bracket handled by writeLines
os << "\t";
os << "}\n";
} else { } else {
os << name << " = "; 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, bool Settings::updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth)
const std::string &end, u32 tab_depth)
{ {
SettingEntries::const_iterator it; SettingEntries::const_iterator it;
std::set<std::string> present_entries; std::set<std::string> present_entries;
@ -220,11 +252,11 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
// in the object if existing // in the object if existing
while (is.good() && !end_found) { while (is.good() && !end_found) {
std::getline(is, line); std::getline(is, line);
SettingsParseEvent event = parseConfigObject(line, end, name, value); SettingsParseEvent event = parseConfigObject(line, name, value);
switch (event) { switch (event) {
case SPE_END: case SPE_END:
os << line << (is.eof() ? "" : "\n"); // Skip end tag. Append later.
end_found = true; end_found = true;
break; break;
case SPE_MULTILINE: 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) { if (it != m_settings.end() && it->second.is_group) {
os << line << "\n"; os << line << "\n";
sanity_check(it->second.group != NULL); sanity_check(it->second.group != NULL);
was_modified |= it->second.group->updateConfigObject(is, os, was_modified |= it->second.group->updateConfigObject(is, os, tab_depth + 1);
"}", tab_depth + 1);
} else if (it == m_settings.end()) { } else if (it == m_settings.end()) {
// Remove by skipping // Remove by skipping
was_modified = true; was_modified = true;
Settings removed_group; // Move 'is' to group end Settings removed_group("}"); // Move 'is' to group end
std::stringstream ss; std::stringstream ss;
removed_group.updateConfigObject(is, ss, "}", tab_depth + 1); removed_group.updateConfigObject(is, ss, tab_depth + 1);
break; break;
} else { } else {
printEntry(os, name, it->second, tab_depth); 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 // 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) { for (it = m_settings.begin(); it != m_settings.end(); ++it) {
if (present_entries.find(it->first) != present_entries.end()) 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; was_modified = true;
} }
// Append ending tag
if (!m_end_tag.empty()) {
os << m_end_tag << "\n";
was_modified |= !end_found;
}
return was_modified; return was_modified;
} }
@ -293,7 +333,7 @@ bool Settings::updateConfigFile(const char *filename)
std::ifstream is(filename); std::ifstream is(filename);
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
bool was_modified = updateConfigObject(is, os, ""); bool was_modified = updateConfigObject(is, os);
is.close(); is.close();
if (!was_modified) if (!was_modified)
@ -366,29 +406,37 @@ bool Settings::parseCommandLine(int argc, char *argv[],
* Getters * * Getters *
***********/ ***********/
Settings *Settings::getParent() const
const SettingsEntry &Settings::getEntry(const std::string &name) 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; for (int i = (int)m_settingslayer - 1; i >= 0; --i) {
if ((n = m_settings.find(name)) == m_settings.end()) { if (s_layers[i])
if ((n = m_defaults.find(name)) == m_defaults.end()) return s_layers[i];
throw SettingNotFoundException("Setting [" + name + "] not found.");
} }
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; SettingEntries::const_iterator n;
if ((n = m_defaults.find(name)) == m_defaults.end()) { if ((n = m_settings.find(name)) != m_settings.end())
throw SettingNotFoundException("Setting [" + name + "] not found."); 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 std::string &Settings::getDefault(const std::string &name) const
{ {
const SettingsEntry &entry = getEntryDefault(name); const SettingsEntry *entry;
if (entry.is_group) 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."); 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 *flagmask) const
{ {
u32 flags = 0; u32 flags = 0;
u32 mask_default = 0;
std::string value;
// Read default value (if there is any) // Read default value (if there is any)
if (getDefaultNoEx(name, value)) { if (auto parent = getParent())
flags = std::isdigit(value[0]) flags = parent->getFlagStr(name, flagdesc, flagmask);
? stoi(value)
: readFlagString(value, flagdesc, &mask_default);
}
// Apply custom flags "on top" // Apply custom flags "on top"
value = get(name); if (m_settings.find(name) != m_settings.end()) {
u32 flags_user; std::string value = get(name);
u32 mask_user = U32_MAX; u32 flags_user;
flags_user = std::isdigit(value[0]) u32 mask_user = U32_MAX;
? stoi(value) // Override default flags_user = std::isdigit(value[0])
: readFlagString(value, flagdesc, &mask_user); ? stoi(value) // Override default
: readFlagString(value, flagdesc, &mask_user);
flags &= ~mask_user; flags &= ~mask_user;
flags |= flags_user; flags |= flags_user;
if (flagmask)
if (flagmask) *flagmask |= mask_user;
*flagmask = mask_default | mask_user; }
return flags; 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 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); MutexAutoLock lock(m_mutex);
return (m_settings.find(name) != m_settings.end() || if (m_settings.find(name) != m_settings.end())
m_defaults.find(name) != m_defaults.end()); return true;
if (auto parent = getParent())
return parent->exists(name);
return false;
} }
std::vector<std::string> Settings::getNames() const std::vector<std::string> Settings::getNames() const
{ {
MutexAutoLock lock(m_mutex);
std::vector<std::string> names; std::vector<std::string> names;
for (const auto &settings_it : m_settings) { for (const auto &settings_it : m_settings) {
names.push_back(settings_it.first); 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 bool Settings::getFlag(const std::string &name) const
{ {
try { 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 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)) if (!checkNameValid(name))
return false; return false;
if (!set_group && !checkValueValid(*(const std::string *)data)) if (!set_group && !checkValueValid(*(const std::string *)data))
return false; return false;
Settings *old_group = NULL;
{ {
MutexAutoLock lock(m_mutex); MutexAutoLock lock(m_mutex);
SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name]; SettingsEntry &entry = m_settings[name];
old_group = entry.group; old_group = entry.group;
entry.value = set_group ? "" : *(const std::string *)data; entry.value = set_group ? "" : *(const std::string *)data;
entry.group = set_group ? *(Settings **)data : NULL; entry.group = set_group ? *(Settings **)data : NULL;
entry.is_group = set_group; entry.is_group = set_group;
if (set_group)
entry.group->m_end_tag = "}";
} }
delete old_group; 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) bool Settings::set(const std::string &name, const std::string &value)
{ {
if (!setEntry(name, &value, false, false)) if (!setEntry(name, &value, false))
return false; return false;
doCallbacks(name); 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) 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 // avoid double-free by copying the source
Settings *copy = new Settings(); Settings *copy = new Settings();
*copy = group; *copy = group;
return setEntry(name, &copy, true, false); return setEntry(name, &copy, true);
}
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);
} }
@ -874,8 +914,7 @@ bool Settings::setFlagStr(const std::string &name, u32 flags,
} }
bool Settings::setNoiseParams(const std::string &name, bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np)
const NoiseParams &np, bool set_default)
{ {
Settings *group = new Settings; Settings *group = new Settings;
@ -888,7 +927,7 @@ bool Settings::setNoiseParams(const std::string &name,
group->setFloat("lacunarity", np.lacunarity); group->setFloat("lacunarity", np.lacunarity);
group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags); 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, 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); std::string trimmed_line = trim(line);
@ -933,7 +960,7 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line,
return SPE_NONE; return SPE_NONE;
if (trimmed_line[0] == '#') if (trimmed_line[0] == '#')
return SPE_COMMENT; return SPE_COMMENT;
if (trimmed_line == end) if (trimmed_line == m_end_tag)
return SPE_END; return SPE_END;
size_t pos = trimmed_line.find('='); 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() void Settings::clearNoLock()
{ {
for (SettingEntries::const_iterator it = m_settings.begin(); for (SettingEntries::const_iterator it = m_settings.begin();
it != m_settings.end(); ++it) it != m_settings.end(); ++it)
delete it->second.group; delete it->second.group;
m_settings.clear(); 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, void Settings::setDefault(const std::string &name, const FlagDesc *flagdesc,
u32 flags) u32 flags)
{ {
m_flags[name] = flagdesc; s_flags[name] = flagdesc;
setDefault(name, writeFlagString(flags, flagdesc, U32_MAX)); 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 const FlagDesc *Settings::getFlagDescFallback(const std::string &name) const
{ {
auto it = m_flags.find(name); auto it = s_flags.find(name);
return it == m_flags.end() ? nullptr : it->second; return it == s_flags.end() ? nullptr : it->second;
} }
void Settings::registerChangedCallback(const std::string &name, void Settings::registerChangedCallback(const std::string &name,

View File

@ -30,7 +30,7 @@ class Settings;
struct NoiseParams; struct NoiseParams;
// Global objects // Global objects
extern Settings *g_settings; extern Settings *g_settings; // Same as Settings::getLayer(SL_GLOBAL);
extern std::string g_settings_path; extern std::string g_settings_path;
// Type for a settings changed callback function // Type for a settings changed callback function
@ -60,6 +60,14 @@ enum SettingsParseEvent {
SPE_MULTILINE, SPE_MULTILINE,
}; };
enum SettingsLayer {
SL_DEFAULTS,
SL_GAME,
SL_GLOBAL,
SL_MAP,
SL_TOTAL_COUNT
};
struct ValueSpec { struct ValueSpec {
ValueSpec(ValueType a_type, const char *a_help=NULL) ValueSpec(ValueType a_type, const char *a_help=NULL)
{ {
@ -92,8 +100,13 @@ typedef std::unordered_map<std::string, SettingsEntry> SettingEntries;
class Settings { class Settings {
public: 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();
Settings & operator += (const Settings &other); Settings & operator += (const Settings &other);
@ -110,7 +123,7 @@ public:
// NOTE: Types of allowed_options are ignored. Returns success. // NOTE: Types of allowed_options are ignored. Returns success.
bool parseCommandLine(int argc, char *argv[], bool parseCommandLine(int argc, char *argv[],
std::map<std::string, ValueSpec> &allowed_options); 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; 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 getGroupNoEx(const std::string &name, Settings *&val) const;
bool getNoEx(const std::string &name, std::string &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 getFlag(const std::string &name) const;
bool getU16NoEx(const std::string &name, u16 &val) const; bool getU16NoEx(const std::string &name, u16 &val) const;
bool getS16NoEx(const std::string &name, s16 &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 // N.B. Groups not allocated with new must be set to NULL in the settings
// tree before object destruction. // tree before object destruction.
bool setEntry(const std::string &name, const void *entry, 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 set(const std::string &name, const std::string &value);
bool setDefault(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 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 setBool(const std::string &name, bool value);
bool setS16(const std::string &name, s16 value); bool setS16(const std::string &name, s16 value);
bool setU16(const std::string &name, u16 value); bool setU16(const std::string &name, u16 value);
@ -185,21 +196,16 @@ public:
bool setV3F(const std::string &name, v3f value); bool setV3F(const std::string &name, v3f value);
bool setFlagStr(const std::string &name, u32 flags, bool setFlagStr(const std::string &name, u32 flags,
const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX); const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX);
bool setNoiseParams(const std::string &name, const NoiseParams &np, bool setNoiseParams(const std::string &name, const NoiseParams &np);
bool set_default=false);
// remove a setting // remove a setting
bool remove(const std::string &name); bool remove(const std::string &name);
void clear();
void clearDefaults();
/************** /**************
* Miscellany * * Miscellany *
**************/ **************/
void setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags); 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; const FlagDesc *getFlagDescFallback(const std::string &name) const;
void registerChangedCallback(const std::string &name, void registerChangedCallback(const std::string &name,
@ -215,9 +221,9 @@ private:
***********************/ ***********************/
SettingsParseEvent parseConfigObject(const std::string &line, 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, 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 checkNameValid(const std::string &name);
static bool checkValueValid(const std::string &value); static bool checkValueValid(const std::string &value);
@ -228,9 +234,9 @@ private:
/*********** /***********
* Getters * * Getters *
***********/ ***********/
Settings *getParent() const;
const SettingsEntry &getEntry(const std::string &name) 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. // Allow TestSettings to run sanity checks using private functions.
friend class TestSettings; friend class TestSettings;
@ -242,14 +248,15 @@ private:
void doCallbacks(const std::string &name) const; void doCallbacks(const std::string &name) const;
SettingEntries m_settings; SettingEntries m_settings;
SettingEntries m_defaults;
std::unordered_map<std::string, const FlagDesc *> m_flags;
SettingsCallbackMap m_callbacks; SettingsCallbackMap m_callbacks;
std::string m_end_tag;
mutable std::mutex m_callback_mutex; mutable std::mutex m_callback_mutex;
// All methods that access m_settings/m_defaults directly should lock this. // All methods that access m_settings/m_defaults directly should lock this.
mutable std::mutex m_mutex; 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;
}; };

View File

@ -30,7 +30,7 @@ public:
TestMapSettingsManager() { TestManager::registerTestModule(this); } TestMapSettingsManager() { TestManager::registerTestModule(this); }
const char *getName() { return "TestMapSettingsManager"; } const char *getName() { return "TestMapSettingsManager"; }
void makeUserConfig(Settings *conf); void makeUserConfig();
std::string makeMetaFile(bool make_corrupt); std::string makeMetaFile(bool make_corrupt);
void runTests(IGameDef *gamedef); 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("mg_name", "v7");
conf->set("seed", "5678"); conf->set("seed", "5678");
conf->set("water_level", "20"); conf->set("water_level", "20");
@ -103,12 +106,11 @@ std::string TestMapSettingsManager::makeMetaFile(bool make_corrupt)
void TestMapSettingsManager::testMapSettingsManager() void TestMapSettingsManager::testMapSettingsManager()
{ {
Settings user_settings; makeUserConfig();
makeUserConfig(&user_settings);
std::string test_mapmeta_path = makeMetaFile(false); std::string test_mapmeta_path = makeMetaFile(false);
MapSettingsManager mgr(&user_settings, test_mapmeta_path); MapSettingsManager mgr(test_mapmeta_path);
std::string value; std::string value;
UASSERT(mgr.getMapSetting("mg_name", &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_height", &script_np_height);
mgr.setMapSettingNoiseParams("mgv5_np_factor", &script_np_factor); 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 // Now make our Params and see if the values are correctly sourced
MapgenParams *params = mgr.makeMapgenParams(); MapgenParams *params = mgr.makeMapgenParams();
UASSERT(params->mgtype == MAPGEN_V5); UASSERT(params->mgtype == MAPGEN_V5);
@ -188,50 +196,66 @@ void TestMapSettingsManager::testMapSettingsManager()
void TestMapSettingsManager::testMapMetaSaveLoad() void TestMapSettingsManager::testMapMetaSaveLoad()
{ {
Settings conf;
std::string path = getTestTempDirectory() std::string path = getTestTempDirectory()
+ DIR_DELIM + "foobar" + DIR_DELIM + "map_meta.txt"; + 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 // Create a set of mapgen params and save them to map meta
conf.set("seed", "12345"); {
conf.set("water_level", "5"); conf.set("seed", "12345");
MapSettingsManager mgr1(&conf, path); conf.set("water_level", "5");
MapgenParams *params1 = mgr1.makeMapgenParams(); MapSettingsManager mgr(path);
UASSERT(params1); MapgenParams *params = mgr.makeMapgenParams();
UASSERT(mgr1.saveMapMeta()); UASSERT(params);
params1 = *params;
params1.bparams = nullptr; // No double-free
UASSERT(mgr.saveMapMeta());
}
// Now try loading the map meta to mapgen params // Now try loading the map meta to mapgen params
conf.set("seed", "67890"); {
conf.set("water_level", "32"); conf.set("seed", "67890");
MapSettingsManager mgr2(&conf, path); conf.set("water_level", "32");
UASSERT(mgr2.loadMapMeta()); MapSettingsManager mgr(path);
MapgenParams *params2 = mgr2.makeMapgenParams(); UASSERT(mgr.loadMapMeta());
UASSERT(params2); MapgenParams *params = mgr.makeMapgenParams();
UASSERT(params);
params2 = *params;
params2.bparams = nullptr; // No double-free
}
// Check that both results are correct // Check that both results are correct
UASSERTEQ(u64, params1->seed, 12345); UASSERTEQ(u64, params1.seed, 12345);
UASSERTEQ(s16, params1->water_level, 5); UASSERTEQ(s16, params1.water_level, 5);
UASSERTEQ(u64, params2->seed, 12345); UASSERTEQ(u64, params2.seed, 12345);
UASSERTEQ(s16, params2->water_level, 5); UASSERTEQ(s16, params2.water_level, 5);
} }
void TestMapSettingsManager::testMapMetaFailures() void TestMapSettingsManager::testMapMetaFailures()
{ {
std::string test_mapmeta_path; std::string test_mapmeta_path;
Settings conf;
// Check to see if it'll fail on a non-existent map meta file // 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); MapSettingsManager mgr1(test_mapmeta_path);
UASSERT(!mgr1.loadMapMeta()); UASSERT(!mgr1.loadMapMeta());
}
// Check to see if it'll fail on a corrupt map meta file // 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); MapSettingsManager mgr2(test_mapmeta_path);
UASSERT(!mgr2.loadMapMeta()); UASSERT(!mgr2.loadMapMeta());
}
} }

View File

@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cmath> #include <cmath>
#include "settings.h" #include "settings.h"
#include "defaultsettings.h"
#include "noise.h" #include "noise.h"
class TestSettings : public TestBase { class TestSettings : public TestBase {
@ -31,6 +32,7 @@ public:
void runTests(IGameDef *gamedef); void runTests(IGameDef *gamedef);
void testAllSettings(); void testAllSettings();
void testDefaults();
void testFlagDesc(); void testFlagDesc();
static const char *config_text_before; static const char *config_text_before;
@ -42,6 +44,7 @@ static TestSettings g_test_instance;
void TestSettings::runTests(IGameDef *gamedef) void TestSettings::runTests(IGameDef *gamedef)
{ {
TEST(testAllSettings); TEST(testAllSettings);
TEST(testDefaults);
TEST(testFlagDesc); TEST(testFlagDesc);
} }
@ -70,7 +73,8 @@ const char *TestSettings::config_text_before =
" with leading whitespace!\n" " with leading whitespace!\n"
"\"\"\"\n" "\"\"\"\n"
"np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\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 = const std::string TestSettings::config_text_after =
"leet = 1337\n" "leet = 1337\n"
@ -111,12 +115,34 @@ const std::string TestSettings::config_text_after =
" animals = cute\n" " animals = cute\n"
" num_apples = 4\n" " num_apples = 4\n"
" num_oranges = 53\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() void TestSettings::testAllSettings()
{ {
try { try {
Settings s; Settings s("[dummy_eof_end_tag]");
// Test reading of settings // Test reading of settings
std::istringstream is(config_text_before); std::istringstream is(config_text_before);
@ -197,21 +223,44 @@ void TestSettings::testAllSettings()
is.clear(); is.clear();
is.seekg(0); is.seekg(0);
UASSERT(s.updateConfigObject(is, os, "", 0) == true); 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 // Confirm settings
// This test only works in older C++ versions than C++11 because we use unordered_map Settings s2("[dummy_eof_end_tag]");
UASSERT(os.str() == config_text_after); std::istringstream is(config_text_after, std::ios_base::binary);
#endif s2.parseConfigLines(is);
compare_settings("(main)", &s, &s2);
}
} catch (SettingNotFoundException &e) { } catch (SettingNotFoundException &e) {
UASSERT(!"Setting not found!"); 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() void TestSettings::testFlagDesc()
{ {
Settings s; Settings &s = *Settings::createLayer(SL_GAME);
FlagDesc flagdesc[] = { FlagDesc flagdesc[] = {
{ "biomes", 0x01 }, { "biomes", 0x01 },
{ "trees", 0x02 }, { "trees", 0x02 },
@ -242,4 +291,6 @@ void TestSettings::testFlagDesc()
// Enabled: tables // Enabled: tables
s.set("test_flags", "16"); s.set("test_flags", "16");
UASSERT(s.getFlagStr("test_flags", flagdesc, nullptr) == 0x10); UASSERT(s.getFlagStr("test_flags", flagdesc, nullptr) == 0x10);
delete &s;
} }