code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>

#include "version.h"
#include "settings.h"
#include "serverlist.h"
#include "filesys.h"
#include "porting.h"
#include "log.h"
#include "network/networkprotocol.h"
#include <json/json.h>
#include "convert_json.h"
#include "httpfetch.h"
#include "util/string.h"

namespace ServerList
{

std::string getFilePath()
{
	std::string serverlist_file = g_settings->get("serverlist_file");

	std::string dir_path = "client" DIR_DELIM "serverlist" DIR_DELIM;
	fs::CreateDir(porting::path_user + DIR_DELIM  "client");
	fs::CreateDir(porting::path_user + DIR_DELIM + dir_path);
	return porting::path_user + DIR_DELIM + dir_path + serverlist_file;
}

std::vector<ServerListSpec> getLocal()
{
	std::string path = ServerList::getFilePath();
	std::string liststring;
	if (fs::PathExists(path)) {
		std::ifstream istream(path.c_str());
		if (istream.is_open()) {
			std::ostringstream ostream;
			ostream << istream.rdbuf();
			liststring = ostream.str();
			istream.close();
		}
	}

	return deSerialize(liststring);
}

std::vector<ServerListSpec> getOnline()
{
	std::ostringstream geturl;

	u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
		CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;

	geturl << g_settings->get("serverlist_url") <<
		"/list?proto_version_min=" << proto_version_min <<
		"&proto_version_max=" << CLIENT_PROTOCOL_VERSION_MAX;
	Json::Value root = fetchJsonValue(geturl.str(), NULL);

	std::vector<ServerListSpec> server_list;

	if (!root.isObject()) {
		return server_list;
	}

	root = root["list"];
	if (!root.isArray()) {
		return server_list;
	}

	for (unsigned int i = 0; i < root.size(); i++) {
		if (root[i].isObject()) {
			server_list.push_back(root[i]);
		}
	}

	return server_list;
}

// Delete a server from the local favorites list
bool deleteEntry(const ServerListSpec &server)
{
	std::vector<ServerListSpec> serverlist = ServerList::getLocal();
	for (std::vector<ServerListSpec>::iterator it = serverlist.begin();
			it != serverlist.end();) {
		if ((*it)["address"] == server["address"] &&
				(*it)["port"] == server["port"]) {
			it = serverlist.erase(it);
		} else {
			++it;
		}
	}

	std::string path = ServerList::getFilePath();
	std::ostringstream ss(std::ios_base::binary);
	ss << ServerList::serialize(serverlist);
	if (!fs::safeWriteToFile(path, ss.str()))
		return false;
	return true;
}

// Insert a server to the local favorites list
bool insert(const ServerListSpec &server)
{
	// Remove duplicates
	ServerList::deleteEntry(server);

	std::vector<ServerListSpec> serverlist = ServerList::getLocal();

	// Insert new server at the top of the list
	serverlist.insert(serverlist.begin(), server);

	std::string path = ServerList::getFilePath();
	std::ostringstream ss(std::ios_base::binary);
	ss << ServerList::serialize(serverlist);
	if (!fs::safeWriteToFile(path, ss.str()))
		return false;

	return true;
}

std::vector<ServerListSpec> deSerialize(const std::string &liststring)
{
	std::vector<ServerListSpec> serverlist;
	std::istringstream stream(liststring);
	std::string line, tmp;
	while (std::getline(stream, line)) {
		std::transform(line.begin(), line.end(), line.begin(), ::toupper);
		if (line == "[SERVER]") {
			ServerListSpec server;
			std::getline(stream, tmp);
			server["name"] = tmp;
			std::getline(stream, tmp);
			server["address"] = tmp;
			std::getline(stream, tmp);
			server["port"] = tmp;
			std::getline(stream, tmp);
			server["description"] = tmp;
			serverlist.push_back(server);
		}
	}
	return serverlist;
}

const std::string serialize(const std::vector<ServerListSpec> &serverlist)
{
	std::string liststring;
	for (std::vector<ServerListSpec>::const_iterator it = serverlist.begin();
			it != serverlist.end();
			++it) {
		liststring += "[server]\n";
		liststring += (*it)["name"].asString() + '\n';
		liststring += (*it)["address"].asString() + '\n';
		liststring += (*it)["port"].asString() + '\n';
		liststring += (*it)["description"].asString() + '\n';
		liststring += '\n';
	}
	return liststring;
}

const std::string serializeJson(const std::vector<ServerListSpec> &serverlist)
{
	Json::Value root;
	Json::Value list(Json::arrayValue);
	for (std::vector<ServerListSpec>::const_iterator it = serverlist.begin();
			it != serverlist.end();
			++it) {
		list.append(*it);
	}
	root["list"] = list;
	Json::FastWriter writer;
	return writer.write(root);
}

#if USE_CURL
void sendAnnounce(AnnounceAction action,
		const u16 port,
		const std::vector<std::string> &clients_names,
		const double uptime,
		const u32 game_time,
		const float lag,
		const std::string &gameid,
		const std::string &mg_name,
		const std::vector<ModSpec> &mods,
		bool dedicated)
{
	static const char *aa_names[] = {"start", "update", "delete"};
	Json::Value server;
	server["action"] = aa_names[action];
	server["port"] = port;
	if (g_settings->exists("server_address")) {
		server["address"] = g_settings->get("server_address");
	}
	if (action != AA_DELETE) {
		bool strict_checking = g_settings->getBool("strict_protocol_version_checking");
		server["name"]         = g_settings->get("server_name");
		server["description"]  = g_settings->get("server_description");
		server["version"]      = g_version_string;
		server["proto_min"]    = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MIN;
		server["proto_max"]    = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MAX;
		server["url"]          = g_settings->get("server_url");
		server["creative"]     = g_settings->getBool("creative_mode");
		server["damage"]       = g_settings->getBool("enable_damage");
		server["password"]     = g_settings->getBool("disallow_empty_password");
		server["pvp"]          = g_settings->getBool("enable_pvp");
		server["uptime"]       = (int) uptime;
		server["game_time"]    = game_time;
		server["clients"]      = (int) clients_names.size();
		server["clients_max"]  = g_settings->getU16("max_users");
		server["clients_list"] = Json::Value(Json::arrayValue);
		for (std::vector<std::string>::const_iterator it = clients_names.begin();
				it != clients_names.end();
				++it) {
			server["clients_list"].append(*it);
		}
		if (gameid != "") server["gameid"] = gameid;
	}

	if (action == AA_START) {
		server["dedicated"]         = dedicated;
		server["rollback"]          = g_settings->getBool("enable_rollback_recording");
		server["mapgen"]            = mg_name;
		server["privs"]             = g_settings->get("default_privs");
		server["can_see_far_names"] = g_settings->getS16("player_transfer_distance") <= 0;
		server["mods"]              = Json::Value(Json::arrayValue);
		for (std::vector<ModSpec>::const_iterator it = mods.begin();
				it != mods.end(); ++it) {
			server["mods"].append(it->name);
		}
		actionstream << "Announcing to " << g_settings->get("serverlist_url") << std::endl;
	} else if (action == AA_UPDATE) {
		if (lag)
			server["lag"] = lag;
	}

	Json::FastWriter writer;
	HTTPFetchRequest fetch_request;
	fetch_request.url = g_settings->get("serverlist_url") + std::string("/announce");
	fetch_request.post_fields["json"] = writer.write(server);
	fetch_request.multipart = true;
	httpfetch_async(fetch_request);
}
#endif

} // namespace ServerList


code:
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <iostream>
#include "config.h"
#include "mods.h"
#include <json/json.h>

#ifndef SERVERLIST_HEADER
#define SERVERLIST_HEADER

typedef Json::Value ServerListSpec;

namespace ServerList
{
std::vector<ServerListSpec> getLocal();
std::vector<ServerListSpec> getOnline();

bool deleteEntry(const ServerListSpec &server);
bool insert(const ServerListSpec &server);

std::vector<ServerListSpec> deSerialize(const std::string &liststring);
const std::string serialize(const std::vector<ServerListSpec> &serverlist);
std::vector<ServerListSpec> deSerializeJson(const std::string &liststring);
const std::string serializeJson(const std::vector<ServerListSpec> &serverlist);

#if USE_CURL
enum AnnounceAction {AA_START, AA_UPDATE, AA_DELETE};
void sendAnnounce(AnnounceAction, u16 port,
		const std::vector<std::string> &clients_names = std::vector<std::string>(),
		double uptime = 0, u32 game_time = 0, float lag = 0,
		const std::string &gameid = "", const std::string &mg_name = "",
		const std::vector<ModSpec> &mods = std::vector<ModSpec>(),
		bool dedicated = false);
#endif

} // namespace ServerList

#endif

repo_name: pgimeno/minetest

license: unknown


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "serverobject.h"
#include <fstream>
#include "inventory.h"
#include "constants.h" // BS

ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos):
	ActiveObject(0),
	m_known_by_count(0),
	m_pending_removal(false),
	m_pending_deactivation(false),
	m_static_exists(false),
	m_static_block(1337,1337,1337),
	m_env(env),
	m_base_position(pos)
{
}

ServerActiveObject::~ServerActiveObject()
{
}

ServerActiveObject* ServerActiveObject::create(ActiveObjectType type,
		ServerEnvironment *env, u16 id, v3f pos,
		const std::string &data)
{
	// Find factory function
	std::map<u16, Factory>::iterator n;
	n = m_types.find(type);
	if(n == m_types.end()) {
		// These are 0.3 entity types, return without error.
		if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) {
			return NULL;
		}

		// If factory is not found, just return.
		warningstream<<"ServerActiveObject: No factory for type="
				<<type<<std::endl;
		return NULL;
	}

	Factory f = n->second;
	ServerActiveObject *object = (*f)(env, pos, data);
	return object;
}

void ServerActiveObject::registerType(u16 type, Factory f)
{
	std::map<u16, Factory>::iterator n;
	n = m_types.find(type);
	if(n != m_types.end())
		return;
	m_types[type] = f;
}

float ServerActiveObject::getMinimumSavedMovement()
{
	return 2.0*BS;
}

ItemStack ServerActiveObject::getWieldedItem() const
{
	const Inventory *inv = getInventory();
	if(inv)
	{
		const InventoryList *list = inv->getList(getWieldList());
		if(list && (getWieldIndex() < (s32)list->getSize()))
			return list->getItem(getWieldIndex());
	}
	return ItemStack();
}

bool ServerActiveObject::setWieldedItem(const ItemStack &item)
{
	if(Inventory *inv = getInventory()) {
		if (InventoryList *list = inv->getList(getWieldList())) {
			list->changeItem(getWieldIndex(), item);
			return true;
		}
	}
	return false;
}


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef SERVEROBJECT_HEADER
#define SERVEROBJECT_HEADER

#include "irrlichttypes_bloated.h"
#include "activeobject.h"
#include "inventorymanager.h"
#include "itemgroup.h"
#include "util/container.h"

/*

Some planning
-------------

* Server environment adds an active object, which gets the id 1
* The active object list is scanned for each client once in a while,
  and it finds out what objects have been added that are not known
  by the client yet. This scan is initiated by the Server class and
  the result ends up directly to the server.
* A network packet is created with the info and sent to the client.
* Environment converts objects to static data and static data to
  objects, based on how close players are to them.

*/

class ServerEnvironment;
struct ItemStack;
struct ToolCapabilities;
struct ObjectProperties;

class ServerActiveObject : public ActiveObject
{
public:
	/*
		NOTE: m_env can be NULL, but step() isn't called if it is.
		Prototypes are used that way.
	*/
	ServerActiveObject(ServerEnvironment *env, v3f pos);
	virtual ~ServerActiveObject();

	virtual ActiveObjectType getSendType() const
	{ return getType(); }

	// Called after id has been set and has been inserted in environment
	virtual void addedToEnvironment(u32 dtime_s){};
	// Called before removing from environment
	virtual void removingFromEnvironment(){};
	// Returns true if object's deletion is the job of the
	// environment
	virtual bool environmentDeletes() const
	{ return true; }

	// Create a certain type of ServerActiveObject
	static ServerActiveObject* create(ActiveObjectType type,
			ServerEnvironment *env, u16 id, v3f pos,
			const std::string &data);

	/*
		Some simple getters/setters
	*/
	v3f getBasePosition(){ return m_base_position; }
	void setBasePosition(v3f pos){ m_base_position = pos; }
	ServerEnvironment* getEnv(){ return m_env; }

	/*
		Some more dynamic interface
	*/

	virtual void setPos(const v3f &pos)
		{ setBasePosition(pos); }
	// continuous: if true, object does not stop immediately at pos
	virtual void moveTo(v3f pos, bool continuous)
		{ setBasePosition(pos); }
	// If object has moved less than this and data has not changed,
	// saving to disk may be omitted
	virtual float getMinimumSavedMovement();

	virtual std::string getDescription(){return "SAO";}

	/*
		Step object in time.
		Messages added to messages are sent to client over network.

		send_recommended:
			True at around 5-10 times a second, same for all objects.
			This is used to let objects send most of the data at the
			same time so that the data can be combined in a single
			packet.
	*/
	virtual void step(float dtime, bool send_recommended){}

	/*
		The return value of this is passed to the client-side object
		when it is created
	*/
	virtual std::string getClientInitializationData(u16 protocol_version){return "";}

	/*
		The return value of this is passed to the server-side object
		when it is created (converted from static to active - actually
		the data is the static form)
	*/
	virtual void getStaticData(std::string *result) const
	{
		assert(isStaticAllowed());
		*result = "";
	}
	/*
		Return false in here to never save and instead remove object
		on unload. getStaticData() will not be called in that case.
	*/
	virtual bool isStaticAllowed() const
	{return true;}

	// Returns tool wear
	virtual int punch(v3f dir,
			const ToolCapabilities *toolcap=NULL,
			ServerActiveObject *puncher=NULL,
			float time_from_last_punch=1000000)
	{ return 0; }
	virtual void rightClick(ServerActiveObject *clicker)
	{}
	virtual void setHP(s16 hp)
	{}
	virtual s16 getHP() const
	{ return 0; }

	virtual void setArmorGroups(const ItemGroupList &armor_groups)
	{}
	virtual const ItemGroupList &getArmorGroups()
	{ static const ItemGroupList rv; return rv; }
	virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity)
	{}
	virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
	{}
	virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop)
	{}
	virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation)
	{}
	virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation)
	{}
	virtual void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
	{}
	virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation)
	{}
	virtual void addAttachmentChild(int child_id)
	{}
	virtual void removeAttachmentChild(int child_id)
	{}
	virtual const UNORDERED_SET<int> &getAttachmentChildIds()
	{ static const UNORDERED_SET<int> rv; return rv; }
	virtual ObjectProperties* accessObjectProperties()
	{ return NULL; }
	virtual void notifyObjectPropertiesModified()
	{}

	// Inventory and wielded item
	virtual Inventory* getInventory()
	{ return NULL; }
	virtual const Inventory* getInventory() const
	{ return NULL; }
	virtual InventoryLocation getInventoryLocation() const
	{ return InventoryLocation(); }
	virtual void setInventoryModified()
	{}
	virtual std::string getWieldList() const
	{ return ""; }
	virtual int getWieldIndex() const
	{ return 0; }
	virtual ItemStack getWieldedItem() const;
	virtual bool setWieldedItem(const ItemStack &item);
	inline void attachParticleSpawner(u32 id)
	{
		m_attached_particle_spawners.insert(id);
	}
	inline void detachParticleSpawner(u32 id)
	{
		m_attached_particle_spawners.erase(id);
	}

	/*
		Number of players which know about this object. Object won't be
		deleted until this is 0 to keep the id preserved for the right
		object.
	*/
	u16 m_known_by_count;

	/*
		- Whether this object is to be removed when nobody knows about
		  it anymore.
		- Removal is delayed to preserve the id for the time during which
		  it could be confused to some other object by some client.
		- This is usually set to true by the step() method when the object wants
		  to be deleted but can be set by anything else too.
	*/
	bool m_pending_removal = false;

	/*
		Same purpose as m_pending_removal but for deactivation.
		deactvation = save static data in block, remove active object

		If this is set alongside with m_pending_removal, removal takes
		priority.
	*/
	bool m_pending_deactivation = false;

	/*
		A getter that unifies the above to answer the question:
		"Can the environment still interact with this object?"
	*/
	inline bool isGone() const
	{ return m_pending_removal || m_pending_deactivation; }

	/*
		Whether the object's static data has been stored to a block
	*/
	bool m_static_exists;
	/*
		The block from which the object was loaded from, and in which
		a copy of the static data resides.
	*/
	v3s16 m_static_block;

	/*
		Queue of messages to be sent to the client
	*/
	std::queue<ActiveObjectMessage> m_messages_out;

protected:
	// Used for creating objects based on type
	typedef ServerActiveObject* (*Factory)
			(ServerEnvironment *env, v3f pos,
			const std::string &data);
	static void registerType(u16 type, Factory f);

	ServerEnvironment *m_env;
	v3f m_base_position;
	UNORDERED_SET<u32> m_attached_particle_spawners;

private:
	// Used for creating objects based on type
	static std::map<u16, Factory> m_types;
};

#endif


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "settings.h"
#include "irrlichttypes_bloated.h"
#include "exceptions.h"
#include "threading/mutex_auto_lock.h"
#include "util/strfnd.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include "debug.h"
#include "log.h"
#include "util/serialize.h"
#include "filesys.h"
#include "noise.h"
#include <cctype>
#include <algorithm>

static Settings main_settings;
Settings *g_settings = &main_settings;
std::string g_settings_path;

Settings::~Settings()
{
	clear();
}

Settings & Settings::operator += (const Settings &other)
{
	update(other);

	return *this;
}

Settings & Settings::operator = (const Settings &other)
{
	if (&other == this)
		return *this;

	MutexAutoLock lock(m_mutex);
	MutexAutoLock lock2(other.m_mutex);

	clearNoLock();
	updateNoLock(other);

	return *this;
}

bool Settings::checkNameValid(const std::string &name)
{
	bool valid = name.find_first_of("=\"{}#") == std::string::npos;
	if (valid) valid = trim(name) == name;
	if (!valid) {
		errorstream << "Invalid setting name \"" << name << "\""
			<< std::endl;
		return false;
	}
	return true;
}

bool Settings::checkValueValid(const std::string &value)
{
	if (value.substr(0, 3) == "\"\"\"" ||
		value.find("\n\"\"\"") != std::string::npos) {
		errorstream << "Invalid character sequence '\"\"\"' found in"
			" setting value!" << std::endl;
		return false;
	}
	return true;
}

std::string Settings::getMultiline(std::istream &is, size_t *num_lines)
{
	size_t lines = 1;
	std::string value;
	std::string line;

	while (is.good()) {
		lines++;
		std::getline(is, line);
		if (line == "\"\"\"")
			break;
		value += line;
		value.push_back('\n');
	}

	size_t len = value.size();
	if (len)
		value.erase(len - 1);

	if (num_lines)
		*num_lines = lines;

	return value;
}

bool Settings::readConfigFile(const char *filename)
{
	std::ifstream is(filename);
	if (!is.good())
		return false;

	return parseConfigLines(is, "");
}

bool Settings::parseConfigLines(std::istream &is, const std::string &end)
{
	MutexAutoLock lock(m_mutex);

	std::string line, name, value;

	while (is.good()) {
		std::getline(is, line);
		SettingsParseEvent event = parseConfigObject(line, end, name, value);

		switch (event) {
		case SPE_NONE:
		case SPE_INVALID:
		case SPE_COMMENT:
			break;
		case SPE_KVPAIR:
			m_settings[name] = SettingsEntry(value);
			break;
		case SPE_END:
			return true;
		case SPE_GROUP: {
			Settings *group = new Settings;
			if (!group->parseConfigLines(is, "}")) {
				delete group;
				return false;
			}
			m_settings[name] = SettingsEntry(group);
			break;
		}
		case SPE_MULTILINE:
			m_settings[name] = SettingsEntry(getMultiline(is));
			break;
		}
	}

	return end.empty();
}

void Settings::writeLines(std::ostream &os, u32 tab_depth) const
{
	MutexAutoLock lock(m_mutex);

	for (SettingEntries::const_iterator it = m_settings.begin();
		 	it != m_settings.end(); ++it)
		printEntry(os, it->first, it->second, tab_depth);
}

void Settings::printEntry(std::ostream &os, const std::string &name,
	const SettingsEntry &entry, u32 tab_depth)
{
	for (u32 i = 0; i != tab_depth; i++)
		os << "\t";

	if (entry.is_group) {
		os << name << " = {\n";

		entry.group->writeLines(os, tab_depth + 1);

		for (u32 i = 0; i != tab_depth; i++)
			os << "\t";
		os << "}\n";
	} else {
		os << name << " = ";

		if (entry.value.find('\n') != std::string::npos)
			os << "\"\"\"\n" << entry.value << "\n\"\"\"\n";
		else
			os << entry.value << "\n";
	}
}

bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
	const std::string &end, u32 tab_depth)
{
	SettingEntries::const_iterator it;
	std::set<std::string> present_entries;
	std::string line, name, value;
	bool was_modified = false;
	bool end_found = false;

	// Add any settings that exist in the config file with the current value
	// in the object if existing
	while (is.good() && !end_found) {
		std::getline(is, line);
		SettingsParseEvent event = parseConfigObject(line, end, name, value);

		switch (event) {
		case SPE_END:
			os << line << (is.eof() ? "" : "\n");
			end_found = true;
			break;
		case SPE_MULTILINE:
			value = getMultiline(is);
			/* FALLTHROUGH */
		case SPE_KVPAIR:
			it = m_settings.find(name);
			if (it != m_settings.end() &&
				(it->second.is_group || it->second.value != value)) {
				printEntry(os, name, it->second, tab_depth);
				was_modified = true;
			} else {
				os << line << "\n";
				if (event == SPE_MULTILINE)
					os << value << "\n\"\"\"\n";
			}
			present_entries.insert(name);
			break;
		case SPE_GROUP:
			it = m_settings.find(name);
			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);
			} else {
				printEntry(os, name, it->second, tab_depth);
				was_modified = true;
			}
			present_entries.insert(name);
			break;
		default:
			os << line << (is.eof() ? "" : "\n");
			break;
		}
	}

	// 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())
			continue;

		printEntry(os, it->first, it->second, tab_depth);
		was_modified = true;
	}

	return was_modified;
}

bool Settings::updateConfigFile(const char *filename)
{
	MutexAutoLock lock(m_mutex);

	std::ifstream is(filename);
	std::ostringstream os(std::ios_base::binary);

	bool was_modified = updateConfigObject(is, os, "");
	is.close();

	if (!was_modified)
		return true;

	if (!fs::safeWriteToFile(filename, os.str())) {
		errorstream << "Error writing configuration file: \""
			<< filename << "\"" << std::endl;
		return false;
	}

	return true;
}

bool Settings::parseCommandLine(int argc, char *argv[],
		std::map<std::string, ValueSpec> &allowed_options)
{
	int nonopt_index = 0;
	for (int i = 1; i < argc; i++) {
		std::string arg_name = argv[i];
		if (arg_name.substr(0, 2) != "--") {
			// If option doesn't start with -, read it in as nonoptX
			if (arg_name[0] != '-'){
				std::string name = "nonopt";
				name += itos(nonopt_index);
				set(name, arg_name);
				nonopt_index++;
				continue;
			}
			errorstream << "Invalid command-line parameter \""
					<< arg_name << "\": --<option> expected." << std::endl;
			return false;
		}

		std::string name = arg_name.substr(2);

		std::map<std::string, ValueSpec>::iterator n;
		n = allowed_options.find(name);
		if (n == allowed_options.end()) {
			errorstream << "Unknown command-line parameter \""
					<< arg_name << "\"" << std::endl;
			return false;
		}

		ValueType type = n->second.type;

		std::string value = "";

		if (type == VALUETYPE_FLAG) {
			value = "true";
		} else {
			if ((i + 1) >= argc) {
				errorstream << "Invalid command-line parameter \""
						<< name << "\": missing value" << std::endl;
				return false;
			}
			value = argv[++i];
		}

		set(name, value);
	}

	return true;
}

/***********
 * Getters *
 ***********/

const SettingsEntry &Settings::getEntry(const std::string &name) const
{
	MutexAutoLock lock(m_mutex);

	SettingEntries::const_iterator n;
	if ((n = m_settings.find(name)) == m_settings.end()) {
		if ((n = m_defaults.find(name)) == m_defaults.end())
			throw SettingNotFoundException("Setting [" + name + "] not found.");
	}
	return n->second;
}

const SettingsEntry &Settings::getEntryDefault(const std::string &name) const
{
	MutexAutoLock lock(m_mutex);

	SettingEntries::const_iterator n;
	if ((n = m_defaults.find(name)) == m_defaults.end()) {
		throw SettingNotFoundException("Setting [" + name + "] not found.");
	}
	return n->second;
}

Settings *Settings::getGroup(const std::string &name) const
{
	const SettingsEntry &entry = getEntry(name);
	if (!entry.is_group)
		throw SettingNotFoundException("Setting [" + name + "] is not a group.");
	return entry.group;
}

const std::string &Settings::get(const std::string &name) const
{
	const SettingsEntry &entry = getEntry(name);
	if (entry.is_group)
		throw SettingNotFoundException("Setting [" + name + "] is a group.");
	return entry.value;
}

const std::string &Settings::getDefault(const std::string &name) const
{
	const SettingsEntry &entry = getEntryDefault(name);
	if (entry.is_group)
		throw SettingNotFoundException("Setting [" + name + "] is a group.");
	return entry.value;
}

bool Settings::getBool(const std::string &name) const
{
	return is_yes(get(name));
}

u16 Settings::getU16(const std::string &name) const
{
	return stoi(get(name), 0, 65535);
}

s16 Settings::getS16(const std::string &name) const
{
	return stoi(get(name), -32768, 32767);
}

s32 Settings::getS32(const std::string &name) const
{
	return stoi(get(name));
}

float Settings::getFloat(const std::string &name) const
{
	return stof(get(name));
}

u64 Settings::getU64(const std::string &name) const
{
	u64 value = 0;
	std::string s = get(name);
	std::istringstream ss(s);
	ss >> value;
	return value;
}

v2f Settings::getV2F(const std::string &name) const
{
	v2f value;
	Strfnd f(get(name));
	f.next("(");
	value.X = stof(f.next(","));
	value.Y = stof(f.next(")"));
	return value;
}

v3f Settings::getV3F(const std::string &name) const
{
	v3f value;
	Strfnd f(get(name));
	f.next("(");
	value.X = stof(f.next(","));
	value.Y = stof(f.next(","));
	value.Z = stof(f.next(")"));
	return value;
}

u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
	u32 *flagmask) const
{
	std::string val = get(name);
	return std::isdigit(val[0])
		? stoi(val)
		: readFlagString(val, flagdesc, flagmask);
}

// N.B. if getStruct() is used to read a non-POD aggregate type,
// the behavior is undefined.
bool Settings::getStruct(const std::string &name, const std::string &format,
	void *out, size_t olen) const
{
	std::string valstr;

	try {
		valstr = get(name);
	} catch (SettingNotFoundException &e) {
		return false;
	}

	if (!deSerializeStringToStruct(valstr, format, out, olen))
		return false;

	return true;
}

bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
{
	return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np);
}

bool Settings::getNoiseParamsFromValue(const std::string &name,
	NoiseParams &np) const
{
	std::string value;

	if (!getNoEx(name, value))
		return false;

	Strfnd f(value);

	np.offset   = stof(f.next(","));
	np.scale    = stof(f.next(","));
	f.next("(");
	np.spread.X = stof(f.next(","));
	np.spread.Y = stof(f.next(","));
	np.spread.Z = stof(f.next(")"));
	f.next(",");
	np.seed     = stoi(f.next(","));
	np.octaves  = stoi(f.next(","));
	np.persist  = stof(f.next(","));

	std::string optional_params = f.next("");
	if (optional_params != "")
		np.lacunarity = stof(optional_params);

	return true;
}

bool Settings::getNoiseParamsFromGroup(const std::string &name,
	NoiseParams &np) const
{
	Settings *group = NULL;

	if (!getGroupNoEx(name, group))
		return false;

	group->getFloatNoEx("offset",      np.offset);
	group->getFloatNoEx("scale",       np.scale);
	group->getV3FNoEx("spread",        np.spread);
	group->getS32NoEx("seed",          np.seed);
	group->getU16NoEx("octaves",       np.octaves);
	group->getFloatNoEx("persistence", np.persist);
	group->getFloatNoEx("lacunarity",  np.lacunarity);

	np.flags = 0;
	if (!group->getFlagStrNoEx("flags", np.flags, flagdesc_noiseparams))
		np.flags = NOISE_FLAG_DEFAULTS;

	return true;
}

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());
}

std::vector<std::string> Settings::getNames() const
{
	std::vector<std::string> names;
	for (SettingEntries::const_iterator i = m_settings.begin();
		 	i != m_settings.end(); ++i) {
		names.push_back(i->first);
	}
	return names;
}

/***************************************
 * Getters that don't throw exceptions *
 ***************************************/

bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const
{
	try {
		val = getEntry(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const
{
	try {
		val = getEntryDefault(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const
{
	try {
		val = getGroup(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getNoEx(const std::string &name, std::string &val) const
{
	try {
		val = get(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

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 {
		return getBool(name);
	} catch(SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getFloatNoEx(const std::string &name, float &val) const
{
	try {
		val = getFloat(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getU16NoEx(const std::string &name, u16 &val) const
{
	try {
		val = getU16(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getS16NoEx(const std::string &name, s16 &val) const
{
	try {
		val = getS16(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getS32NoEx(const std::string &name, s32 &val) const
{
	try {
		val = getS32(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getU64NoEx(const std::string &name, u64 &val) const
{
	try {
		val = getU64(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getV2FNoEx(const std::string &name, v2f &val) const
{
	try {
		val = getV2F(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

bool Settings::getV3FNoEx(const std::string &name, v3f &val) const
{
	try {
		val = getV3F(name);
		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

// N.B. getFlagStrNoEx() does not set val, but merely modifies it.  Thus,
// val must be initialized before using getFlagStrNoEx().  The intention of
// this is to simplify modifying a flags field from a default value.
bool Settings::getFlagStrNoEx(const std::string &name, u32 &val,
	FlagDesc *flagdesc) const
{
	try {
		u32 flags, flagmask;

		flags = getFlagStr(name, flagdesc, &flagmask);

		val &= ~flagmask;
		val |=  flags;

		return true;
	} catch (SettingNotFoundException &e) {
		return false;
	}
}

/***********
 * Setters *
 ***********/

bool Settings::setEntry(const std::string &name, const void *data,
	bool set_group, bool set_default)
{
	Settings *old_group = NULL;

	if (!checkNameValid(name))
		return false;
	if (!set_group && !checkValueValid(*(const std::string *)data))
		return false;

	{
		MutexAutoLock lock(m_mutex);

		SettingsEntry &entry = set_default ? m_defaults[name] : 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;
	}

	delete old_group;

	return true;
}

bool Settings::set(const std::string &name, const std::string &value)
{
	if (!setEntry(name, &value, false, false))
		return false;

	doCallbacks(name);
	return true;
}

bool Settings::setDefault(const std::string &name, const std::string &value)
{
	return setEntry(name, &value, false, true);
}

bool Settings::setGroup(const std::string &name, Settings *group)
{
	return setEntry(name, &group, true, false);
}

bool Settings::setGroupDefault(const std::string &name, Settings *group)
{
	return setEntry(name, &group, true, true);
}

bool Settings::setBool(const std::string &name, bool value)
{
	return set(name, value ? "true" : "false");
}

bool Settings::setS16(const std::string &name, s16 value)
{
	return set(name, itos(value));
}

bool Settings::setU16(const std::string &name, u16 value)
{
	return set(name, itos(value));
}

bool Settings::setS32(const std::string &name, s32 value)
{
	return set(name, itos(value));
}

bool Settings::setU64(const std::string &name, u64 value)
{
	std::ostringstream os;
	os << value;
	return set(name, os.str());
}

bool Settings::setFloat(const std::string &name, float value)
{
	return set(name, ftos(value));
}

bool Settings::setV2F(const std::string &name, v2f value)
{
	std::ostringstream os;
	os << "(" << value.X << "," << value.Y << ")";
	return set(name, os.str());
}

bool Settings::setV3F(const std::string &name, v3f value)
{
	std::ostringstream os;
	os << "(" << value.X << "," << value.Y << "," << value.Z << ")";
	return set(name, os.str());
}

bool Settings::setFlagStr(const std::string &name, u32 flags,
	const FlagDesc *flagdesc, u32 flagmask)
{
	return set(name, writeFlagString(flags, flagdesc, flagmask));
}

bool Settings::setStruct(const std::string &name, const std::string &format,
	void *value)
{
	std::string structstr;
	if (!serializeStructToString(&structstr, format, value))
		return false;

	return set(name, structstr);
}

bool Settings::setNoiseParams(const std::string &name,
	const NoiseParams &np, bool set_default)
{
	Settings *group = new Settings;

	group->setFloat("offset",      np.offset);
	group->setFloat("scale",       np.scale);
	group->setV3F("spread",        np.spread);
	group->setS32("seed",          np.seed);
	group->setU16("octaves",       np.octaves);
	group->setFloat("persistence", np.persist);
	group->setFloat("lacunarity",  np.lacunarity);
	group->setFlagStr("flags",     np.flags, flagdesc_noiseparams, np.flags);

	return setEntry(name, &group, true, set_default);
}

bool Settings::remove(const std::string &name)
{
	MutexAutoLock lock(m_mutex);

	SettingEntries::iterator it = m_settings.find(name);
	if (it != m_settings.end()) {
		delete it->second.group;
		m_settings.erase(it);
		return true;
	} else {
		return false;
	}
}

void Settings::clear()
{
	MutexAutoLock lock(m_mutex);
	clearNoLock();
}

void Settings::clearDefaults()
{
	MutexAutoLock lock(m_mutex);
	clearDefaultsNoLock();
}

void Settings::updateValue(const Settings &other, const std::string &name)
{
	if (&other == this)
		return;

	MutexAutoLock lock(m_mutex);

	try {
		std::string val = other.get(name);
		m_settings[name] = val;
	} catch (SettingNotFoundException &e) {
	}
}

void Settings::update(const Settings &other)
{
	if (&other == this)
		return;

	MutexAutoLock lock(m_mutex);
	MutexAutoLock lock2(other.m_mutex);

	updateNoLock(other);
}

SettingsParseEvent Settings::parseConfigObject(const std::string &line,
	const std::string &end, std::string &name, std::string &value)
{
	std::string trimmed_line = trim(line);

	if (trimmed_line.empty())
		return SPE_NONE;
	if (trimmed_line[0] == '#')
		return SPE_COMMENT;
	if (trimmed_line == end)
		return SPE_END;

	size_t pos = trimmed_line.find('=');
	if (pos == std::string::npos)
		return SPE_INVALID;

	name  = trim(trimmed_line.substr(0, pos));
	value = trim(trimmed_line.substr(pos + 1));

	if (value == "{")
		return SPE_GROUP;
	if (value == "\"\"\"")
		return SPE_MULTILINE;

	return SPE_KVPAIR;
}

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::registerChangedCallback(const std::string &name,
	SettingsChangedCallback cbf, void *userdata)
{
	MutexAutoLock lock(m_callback_mutex);
	m_callbacks[name].push_back(std::make_pair(cbf, userdata));
}

void Settings::deregisterChangedCallback(const std::string &name,
	SettingsChangedCallback cbf, void *userdata)
{
	MutexAutoLock lock(m_callback_mutex);
	SettingsCallbackMap::iterator it_cbks = m_callbacks.find(name);

	if (it_cbks != m_callbacks.end()) {
		SettingsCallbackList &cbks = it_cbks->second;

		SettingsCallbackList::iterator position =
			std::find(cbks.begin(), cbks.end(), std::make_pair(cbf, userdata));

		if (position != cbks.end())
			cbks.erase(position);
	}
}

void Settings::doCallbacks(const std::string &name) const
{
	MutexAutoLock lock(m_callback_mutex);

	SettingsCallbackMap::const_iterator it_cbks = m_callbacks.find(name);
	if (it_cbks != m_callbacks.end()) {
		SettingsCallbackList::const_iterator it;
		for (it = it_cbks->second.begin(); it != it_cbks->second.end(); ++it)
			(it->first)(name, it->second);
	}
}


code:
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef SETTINGS_HEADER
#define SETTINGS_HEADER

#include "irrlichttypes_bloated.h"
#include "util/string.h"
#include "threading/mutex.h"
#include <string>
#include "util/cpp11_container.h"
#include <list>
#include <set>

class Settings;
struct NoiseParams;

// Global objects
extern Settings *g_settings;
extern std::string g_settings_path;

// Type for a settings changed callback function
typedef void (*SettingsChangedCallback)(const std::string &name, void *data);

typedef std::vector<
	std::pair<
		SettingsChangedCallback,
		void *
	>
> SettingsCallbackList;

typedef UNORDERED_MAP<std::string, SettingsCallbackList> SettingsCallbackMap;

enum ValueType {
	VALUETYPE_STRING,
	VALUETYPE_FLAG // Doesn't take any arguments
};

enum SettingsParseEvent {
	SPE_NONE,
	SPE_INVALID,
	SPE_COMMENT,
	SPE_KVPAIR,
	SPE_END,
	SPE_GROUP,
	SPE_MULTILINE,
};

struct ValueSpec {
	ValueSpec(ValueType a_type, const char *a_help=NULL)
	{
		type = a_type;
		help = a_help;
	}

	ValueType type;
	const char *help;
};

struct SettingsEntry {
	SettingsEntry() :
		group(NULL),
		is_group(false)
	{}

	SettingsEntry(const std::string &value_) :
		value(value_),
		group(NULL),
		is_group(false)
	{}

	SettingsEntry(Settings *group_) :
		group(group_),
		is_group(true)
	{}

	std::string value;
	Settings *group;
	bool is_group;
};

typedef UNORDERED_MAP<std::string, SettingsEntry> SettingEntries;

class Settings {
public:
	Settings() {}
	~Settings();

	Settings & operator += (const Settings &other);
	Settings & operator = (const Settings &other);

	/***********************
	 * Reading and writing *
	 ***********************/

	// Read configuration file.  Returns success.
	bool readConfigFile(const char *filename);
	//Updates configuration file.  Returns success.
	bool updateConfigFile(const char *filename);
	// 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 = "");
	void writeLines(std::ostream &os, u32 tab_depth=0) const;

	SettingsParseEvent parseConfigObject(const std::string &line,
		const std::string &end, std::string &name, std::string &value);
	bool updateConfigObject(std::istream &is, std::ostream &os,
		const std::string &end, u32 tab_depth=0);

	static bool checkNameValid(const std::string &name);
	static bool checkValueValid(const std::string &value);
	static std::string getMultiline(std::istream &is, size_t *num_lines=NULL);
	static void printEntry(std::ostream &os, const std::string &name,
		const SettingsEntry &entry, u32 tab_depth=0);

	/***********
	 * Getters *
	 ***********/

	const SettingsEntry &getEntry(const std::string &name) const;
	const SettingsEntry &getEntryDefault(const std::string &name) const;
	Settings *getGroup(const std::string &name) const;
	const std::string &get(const std::string &name) const;
	const std::string &getDefault(const std::string &name) const;
	bool getBool(const std::string &name) const;
	u16 getU16(const std::string &name) const;
	s16 getS16(const std::string &name) const;
	s32 getS32(const std::string &name) const;
	u64 getU64(const std::string &name) const;
	float getFloat(const std::string &name) const;
	v2f getV2F(const std::string &name) const;
	v3f getV3F(const std::string &name) const;
	u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc,
			u32 *flagmask) const;
	// N.B. if getStruct() is used to read a non-POD aggregate type,
	// the behavior is undefined.
	bool getStruct(const std::string &name, const std::string &format,
			void *out, size_t olen) const;
	bool getNoiseParams(const std::string &name, NoiseParams &np) const;
	bool getNoiseParamsFromValue(const std::string &name, NoiseParams &np) const;
	bool getNoiseParamsFromGroup(const std::string &name, NoiseParams &np) const;

	// return all keys used
	std::vector<std::string> getNames() const;
	bool exists(const std::string &name) const;

	/***************************************
	 * Getters that don't throw exceptions *
	 ***************************************/

	bool getEntryNoEx(const std::string &name, SettingsEntry &val) const;
	bool getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const;
	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;
	bool getS32NoEx(const std::string &name, s32 &val) const;
	bool getU64NoEx(const std::string &name, u64 &val) const;
	bool getFloatNoEx(const std::string &name, float &val) const;
	bool getV2FNoEx(const std::string &name, v2f &val) const;
	bool getV3FNoEx(const std::string &name, v3f &val) const;
	// N.B. getFlagStrNoEx() does not set val, but merely modifies it.  Thus,
	// val must be initialized before using getFlagStrNoEx().  The intention of
	// this is to simplify modifying a flags field from a default value.
	bool getFlagStrNoEx(const std::string &name, u32 &val, FlagDesc *flagdesc) const;

	/***********
	 * Setters *
	 ***********/

	// 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(const std::string &name, const std::string &value);
	bool setDefault(const std::string &name, const std::string &value);
	bool setGroup(const std::string &name, Settings *group);
	bool setGroupDefault(const std::string &name, 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);
	bool setS32(const std::string &name, s32 value);
	bool setU64(const std::string &name, u64 value);
	bool setFloat(const std::string &name, float value);
	bool setV2F(const std::string &name, v2f value);
	bool setV3F(const std::string &name, v3f value);
	bool setFlagStr(const std::string &name, u32 flags,
		const FlagDesc *flagdesc, u32 flagmask);
	bool setNoiseParams(const std::string &name, const NoiseParams &np,
		bool set_default=false);
	// N.B. if setStruct() is used to write a non-POD aggregate type,
	// the behavior is undefined.
	bool setStruct(const std::string &name, const std::string &format, void *value);

	// remove a setting
	bool remove(const std::string &name);
	void clear();
	void clearDefaults();
	void updateValue(const Settings &other, const std::string &name);
	void update(const Settings &other);

	void registerChangedCallback(const std::string &name,
		SettingsChangedCallback cbf, void *userdata = NULL);
	void deregisterChangedCallback(const std::string &name,
		SettingsChangedCallback cbf, void *userdata = NULL);

private:
	void updateNoLock(const Settings &other);
	void clearNoLock();
	void clearDefaultsNoLock();

	void doCallbacks(const std::string &name) const;

	SettingEntries m_settings;
	SettingEntries m_defaults;

	SettingsCallbackMap m_callbacks;

	mutable Mutex m_callback_mutex;

	// All methods that access m_settings/m_defaults directly should lock this.
	mutable Mutex m_mutex;

};

#endif