From 6bc4cad0eddd7a7cf593ca1471599e2d75727379 Mon Sep 17 00:00:00 2001
From: ShadowNinja <shadowninja@minetest.net>
Date: Thu, 11 Sep 2014 18:22:05 -0400
Subject: [PATCH] Split settings into seperate source and header files

This also cleans up settings a bit
---
 build/android/jni/Android.mk    |   3 +-
 src/CMakeLists.txt              | 155 +++---
 src/client.cpp                  |  17 +-
 src/clientiface.cpp             |   7 +-
 src/clientmedia.cpp             |   5 +-
 src/content_cao.cpp             |  19 +-
 src/content_mapblock.cpp        |   7 +-
 src/content_sao.cpp             |   5 +-
 src/database-leveldb.cpp        |   5 +-
 src/database-sqlite3.cpp        |   1 +
 src/emerge.cpp                  |   6 +-
 src/environment.cpp             |  33 +-
 src/guiEngine.cpp               |   7 +-
 src/inventorymanager.cpp        |   1 +
 src/keycode.cpp                 |   1 +
 src/map.cpp                     |  30 +-
 src/mapgen.cpp                  |   2 +-
 src/mapgen.h                    |   4 +-
 src/nodedef.cpp                 |   3 +-
 src/player.cpp                  |  22 +-
 src/script/lua_api/l_mapgen.cpp |   2 +
 src/script/lua_api/l_util.cpp   |   3 +-
 src/settings.cpp                | 702 +++++++++++++++++++++++++++
 src/settings.h                  | 836 ++++----------------------------
 src/socket.cpp                  |  29 +-
 src/test.cpp                    |  14 +-
 src/tile.cpp                    |  12 +-
 27 files changed, 993 insertions(+), 938 deletions(-)
 create mode 100644 src/settings.cpp

diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk
index 9b2057781..96693fa4d 100644
--- a/build/android/jni/Android.mk
+++ b/build/android/jni/Android.mk
@@ -206,7 +206,8 @@ LOCAL_SRC_FILES :=                                \
 		jni/src/util/string.cpp                   \
 		jni/src/util/timetaker.cpp                \
 		jni/src/touchscreengui.cpp                \
- 		jni/src/database-leveldb.cpp
+ 		jni/src/database-leveldb.cpp              \
+ 		jni/src/settings.cpp
 
 # lua api
 LOCAL_SRC_FILES +=                                \
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index de1ea2c28..4c1522fac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -333,74 +333,75 @@ add_subdirectory(script)
 add_subdirectory(util)
 
 set(common_SRCS
-	version.cpp
-	rollback_interface.cpp
-	rollback.cpp
-	genericobject.cpp
-	voxelalgorithms.cpp
-	sound.cpp
-	quicktune.cpp
-	subgame.cpp
-	inventorymanager.cpp
-	mods.cpp
+	ban.cpp
+	base64.cpp
+	biome.cpp
+	cavegen.cpp
+	clientiface.cpp
+	collision.cpp
+	connection.cpp
 	content_abm.cpp
+	content_mapnode.cpp
+	content_nodemeta.cpp
+	content_sao.cpp
+	convert_json.cpp
 	craftdef.cpp
-	nameidmapping.cpp
+	database-dummy.cpp
+	database-leveldb.cpp
+	database-redis.cpp
+	database-sqlite3.cpp
+	database.cpp
+	debug.cpp
+	defaultsettings.cpp
+	dungeongen.cpp
+	emerge.cpp
+	environment.cpp
+	filesys.cpp
+	genericobject.cpp
+	gettext.cpp
+	httpfetch.cpp
+	inventory.cpp
+	inventorymanager.cpp
 	itemdef.cpp
-	nodedef.cpp
-	object_properties.cpp
+	light.cpp
 	log.cpp
-	content_sao.cpp
-	emerge.cpp
+	map.cpp
+	mapblock.cpp
 	mapgen.cpp
+	mapgen_singlenode.cpp
 	mapgen_v6.cpp
 	mapgen_v7.cpp
-	mapgen_singlenode.cpp
-	treegen.cpp
-	dungeongen.cpp
-	cavegen.cpp
-	content_nodemeta.cpp
-	content_mapnode.cpp
-	collision.cpp
+	mapnode.cpp
+	mapsector.cpp
+	mods.cpp
+	nameidmapping.cpp
+	nodedef.cpp
 	nodemetadata.cpp
 	nodetimer.cpp
-	serverobject.cpp
 	noise.cpp
+	object_properties.cpp
+	pathfinder.cpp
+	player.cpp
 	porting.cpp
-	tool.cpp
-	defaultsettings.cpp
-	mapnode.cpp
-	voxel.cpp
-	inventory.cpp
-	debug.cpp
+	quicktune.cpp
+	rollback.cpp
+	rollback_interface.cpp
 	serialization.cpp
-	light.cpp
-	filesys.cpp
-	connection.cpp
-	environment.cpp
 	server.cpp
-	clientiface.cpp
-	socket.cpp
-	mapblock.cpp
-	mapsector.cpp
-	map.cpp
-	database.cpp
-	database-dummy.cpp
-	database-leveldb.cpp
-	database-sqlite3.cpp
-	database-redis.cpp
-	player.cpp
-	test.cpp
+	serverlist.cpp
+	serverobject.cpp
+	settings.cpp
 	sha1.cpp
-	base64.cpp
-	ban.cpp
-	biome.cpp
+	socket.cpp
+	sound.cpp
 	staticobject.cpp
-	serverlist.cpp
-	pathfinder.cpp
-	convert_json.cpp
-	gettext.cpp
-	httpfetch.cpp
+	subgame.cpp
+	test.cpp
+	tool.cpp
+	treegen.cpp
+	version.cpp
+	voxel.cpp
+	voxelalgorithms.cpp
 	${JTHREAD_SRCS}
 	${common_SCRIPT_SRCS}
 	${UTIL_SRCS}
@@ -429,38 +430,38 @@ endif()
 set(minetest_SRCS
 	${common_SRCS}
 	${sound_SRCS}
-	localplayer.cpp
-	sky.cpp
-	clientmap.cpp
-	content_cso.cpp
-	content_mapblock.cpp
-	content_cao.cpp
-	mesh.cpp
-	mapblock_mesh.cpp
-	keycode.cpp
 	camera.cpp
-	clouds.cpp
-	particles.cpp
-	clientobject.cpp
 	chat.cpp
-	hud.cpp
-	guiKeyChangeMenu.cpp
-	guiFormSpecMenu.cpp
-	guiTable.cpp
-	guiPasswordChange.cpp
-	guiVolumeChange.cpp
-	guiChatConsole.cpp
 	client.cpp
+	clientmap.cpp
 	clientmedia.cpp
+	clientobject.cpp
+	clouds.cpp
+	content_cao.cpp
+	content_cso.cpp
+	content_mapblock.cpp
+	convert_json.cpp
+	drawscene.cpp
 	filecache.cpp
-	tile.cpp
-	shader.cpp
 	game.cpp
-	main.cpp
+	guiChatConsole.cpp
 	guiEngine.cpp
 	guiFileSelectMenu.cpp
-	convert_json.cpp
-	drawscene.cpp
+	guiFormSpecMenu.cpp
+	guiKeyChangeMenu.cpp
+	guiPasswordChange.cpp
+	guiTable.cpp
+	guiVolumeChange.cpp
+	hud.cpp
+	keycode.cpp
+	localplayer.cpp
+	main.cpp
+	mapblock_mesh.cpp
+	mesh.cpp
+	particles.cpp
+	shader.cpp
+	sky.cpp
+	tile.cpp
 	${minetest_SCRIPT_SRCS}
 )
 list(SORT minetest_SRCS)
diff --git a/src/client.cpp b/src/client.cpp
index accff4b01..5a276e306 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -17,13 +17,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "client.h"
 #include <iostream>
 #include <algorithm>
-#include "clientserver.h"
+#include <sstream>
+#include <IFileSystem.h>
 #include "jthread/jmutexautolock.h"
+#include "util/directiontables.h"
+#include "util/pointedthing.h"
+#include "util/serialize.h"
+#include "util/string.h"
+#include "strfnd.h"
+#include "client.h"
+#include "clientserver.h"
 #include "main.h"
-#include <sstream>
 #include "filesys.h"
 #include "porting.h"
 #include "mapsector.h"
@@ -37,18 +43,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "itemdef.h"
 #include "shader.h"
-#include <IFileSystem.h>
 #include "base64.h"
 #include "clientmap.h"
 #include "clientmedia.h"
 #include "sound.h"
-#include "util/string.h"
 #include "IMeshCache.h"
 #include "serialization.h"
-#include "util/serialize.h"
 #include "config.h"
-#include "util/directiontables.h"
-#include "util/pointedthing.h"
 #include "version.h"
 #include "drawscene.h"
 
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index ebbbc65bc..cd2e2d4d9 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -20,6 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <sstream>
 
 #include "clientiface.h"
+#include "util/numeric.h"
+#include "util/mathconstants.h"
 #include "player.h"
 #include "settings.h"
 #include "mapblock.h"
@@ -28,11 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map.h"
 #include "emerge.h"
 #include "serverobject.h"              // TODO this is used for cleanup of only
-
-#include "util/numeric.h"
-#include "util/mathconstants.h"
-
 #include "main.h"                      // for g_settings
+#include "log.h"
 
 const char *ClientInterface::statenames[] = {
 	"Invalid",
diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp
index e2679ed46..434eeb248 100644
--- a/src/clientmedia.cpp
+++ b/src/clientmedia.cpp
@@ -18,10 +18,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "clientmedia.h"
+#include "util/serialize.h"
+#include "util/string.h"
 #include "httpfetch.h"
 #include "client.h"
 #include "clientserver.h"
 #include "filecache.h"
+#include "filesys.h"
 #include "hex.h"
 #include "sha1.h"
 #include "debug.h"
@@ -29,8 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "settings.h"
 #include "main.h"
-#include "util/serialize.h"
-#include "util/string.h"
 
 static std::string getMediaCacheDir()
 {
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 02622f5b4..d1de23d2a 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -17,14 +17,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <ICameraSceneNode.h>
+#include <ITextSceneNode.h>
+#include <IBillboardSceneNode.h>
+#include <IMeshManipulator.h>
+#include <IAnimatedMeshSceneNode.h>
+#include <IBoneSceneNode.h>
 #include "content_cao.h"
+#include "util/numeric.h" // For IntervalLimiter
+#include "util/serialize.h"
+#include "util/mathconstants.h"
 #include "tile.h"
 #include "environment.h"
 #include "collision.h"
 #include "settings.h"
-#include <ICameraSceneNode.h>
-#include <ITextSceneNode.h>
-#include <IBillboardSceneNode.h>
 #include "serialization.h" // For decompressZlib
 #include "gamedef.h"
 #include "clientobject.h"
@@ -36,15 +42,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "sound.h"
 #include "nodedef.h"
 #include "localplayer.h"
-#include "util/numeric.h" // For IntervalLimiter
-#include "util/serialize.h"
-#include "util/mathconstants.h"
 #include "map.h"
 #include "main.h" // g_settings
 #include "camera.h" // CameraModes
-#include <IMeshManipulator.h>
-#include <IAnimatedMeshSceneNode.h>
-#include <IBoneSceneNode.h>
+#include "log.h"
 
 class Settings;
 struct ToolCapabilities;
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index b7a48d57e..c94ff590e 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -18,15 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "content_mapblock.h"
-
+#include "util/numeric.h"
+#include "util/directiontables.h"
 #include "main.h" // For g_settings
 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
 #include "settings.h"
 #include "nodedef.h"
 #include "tile.h"
 #include "gamedef.h"
-#include "util/numeric.h"
-#include "util/directiontables.h"
+#include "log.h"
+
 
 // Create a cuboid.
 //  collector - the MeshCollector for the resulting polygons
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index 4ee92f4d3..40313f7a5 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "content_sao.h"
+#include "util/serialize.h"
+#include "util/mathconstants.h"
 #include "collision.h"
 #include "environment.h"
 #include "settings.h"
@@ -29,8 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "player.h"
 #include "scripting_game.h"
 #include "genericobject.h"
-#include "util/serialize.h"
-#include "util/mathconstants.h"
+#include "log.h"
 
 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
 
diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp
index d57462be9..1681b0195 100644
--- a/src/database-leveldb.cpp
+++ b/src/database-leveldb.cpp
@@ -20,10 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "config.h"
 
 #if USE_LEVELDB
-/*
-LevelDB databases
-*/
-
 
 #include "database-leveldb.h"
 #include "leveldb/db.h"
@@ -35,6 +31,7 @@ LevelDB databases
 #include "main.h"
 #include "settings.h"
 #include "log.h"
+#include "filesys.h"
 
 #define ENSURE_STATUS_OK(s) \
 	if (!(s).ok()) { \
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index 4d48796fc..8e1501786 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "main.h"
 #include "settings.h"
 #include "log.h"
+#include "filesys.h"
 
 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
 {
diff --git a/src/emerge.cpp b/src/emerge.cpp
index a2b265486..b6e2080a6 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -97,7 +97,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) {
 
 	// if unspecified, leave a proc for the main thread and one for
 	// some other misc thread
-	int nthreads = 0;
+	s16 nthreads = 0;
 	if (!g_settings->getS16NoEx("num_emerge_threads", nthreads))
 		nthreads = porting::getNumberOfProcessors() - 2;
 	if (nthreads < 1)
@@ -117,8 +117,8 @@ EmergeManager::EmergeManager(IGameDef *gamedef) {
 	if (qlimit_generate < 1)
 		qlimit_generate = 1;
 
-	for (int i = 0; i != nthreads; i++)
-		emergethread.push_back(new EmergeThread((Server *)gamedef, i));
+	for (s16 i = 0; i < nthreads; i++)
+		emergethread.push_back(new EmergeThread((Server *) gamedef, i));
 
 	infostream << "EmergeManager: using " << nthreads << " threads" << std::endl;
 }
diff --git a/src/environment.cpp b/src/environment.cpp
index c8af72f6b..66898f012 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -508,38 +508,29 @@ void ServerEnvironment::loadMeta()
 
 	// Open file and deserialize
 	std::ifstream is(path.c_str(), std::ios_base::binary);
-	if(is.good() == false)
-	{
-		infostream<<"ServerEnvironment::loadMeta(): Failed to open "
-				<<path<<std::endl;
+	if (!is.good()) {
+		infostream << "ServerEnvironment::loadMeta(): Failed to open "
+				<< path << std::endl;
 		throw SerializationError("Couldn't load env meta");
 	}
 
 	Settings args;
-	
-	for(;;)
-	{
-		if(is.eof())
-			throw SerializationError
-					("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
-		std::string line;
-		std::getline(is, line);
-		std::string trimmedline = trim(line);
-		if(trimmedline == "EnvArgsEnd")
-			break;
-		args.parseConfigLine(line);
+
+	if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+		throw SerializationError("ServerEnvironment::loadMeta(): "
+				"EnvArgsEnd not found!");
 	}
-	
-	try{
+
+	try {
 		m_game_time = args.getU64("game_time");
-	}catch(SettingNotFoundException &e){
+	} catch (SettingNotFoundException &e) {
 		// Getting this is crucial, otherwise timestamps are useless
 		throw SerializationError("Couldn't load env meta game_time");
 	}
 
-	try{
+	try {
 		m_time_of_day = args.getU64("time_of_day");
-	}catch(SettingNotFoundException &e){
+	} catch (SettingNotFoundException &e) {
 		// This is not as important
 		m_time_of_day = 9000;
 	}
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index e2f1a0aad..41a522caf 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -19,7 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "guiEngine.h"
 
+#include <IGUIStaticText.h>
+#include <ICameraSceneNode.h>
 #include "scripting_mainmenu.h"
+#include "util/numeric.h"
 #include "config.h"
 #include "version.h"
 #include "porting.h"
@@ -31,14 +34,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "sound_openal.h"
 #include "clouds.h"
 #include "httpfetch.h"
-#include "util/numeric.h"
+#include "log.h"
 #ifdef __ANDROID__
 #include "tile.h"
 #include <GLES/gl.h>
 #endif
 
-#include <IGUIStaticText.h>
-#include <ICameraSceneNode.h>
 
 /******************************************************************************/
 /** TextDestGuiEngine                                                         */
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index 1c511e9cb..1ffcc3460 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "craftdef.h"
 #include "rollback_interface.h"
+#include "strfnd.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
diff --git a/src/keycode.cpp b/src/keycode.cpp
index 890c97cc2..c5f102b44 100644
--- a/src/keycode.cpp
+++ b/src/keycode.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "log.h"
 #include "hex.h"
+#include "debug.h"
 
 class UnknownKeycode : public BaseException
 {
diff --git a/src/map.cpp b/src/map.cpp
index 236972ae9..1fe0ca9ad 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -3065,37 +3065,25 @@ void ServerMap::loadMapMeta()
 {
 	DSTACK(__FUNCTION_NAME);
 
-	/*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
-			<<std::endl;*/
-
-	std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
+	std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
 	std::ifstream is(fullpath.c_str(), std::ios_base::binary);
-	if(is.good() == false)
-	{
-		infostream<<"ERROR: ServerMap::loadMapMeta(): "
-				<<"could not open"<<fullpath<<std::endl;
+	if (!is.good()) {
+		errorstream << "ServerMap::loadMapMeta(): "
+				<< "could not open" << fullpath << std::endl;
 		throw FileNotGoodException("Cannot open map metadata");
 	}
 
 	Settings params;
 
-	for(;;)
-	{
-		if(is.eof())
-			throw SerializationError
-					("ServerMap::loadMapMeta(): [end_of_params] not found");
-		std::string line;
-		std::getline(is, line);
-		std::string trimmedline = trim(line);
-		if(trimmedline == "[end_of_params]")
-			break;
-		params.parseConfigLine(line);
+	if (!params.parseConfigLines(is, "[end_of_params]")) {
+		throw SerializationError("ServerMap::loadMapMeta(): "
+				"[end_of_params] not found!");
 	}
 
 	m_emerge->loadParamsFromSettings(&params);
 
-	verbosestream<<"ServerMap::loadMapMeta(): seed="
-		<< m_emerge->params.seed<<std::endl;
+	verbosestream << "ServerMap::loadMapMeta(): seed="
+		<< m_emerge->params.seed << std::endl;
 }
 
 void ServerMap::saveSectorMeta(ServerMapSector *sector)
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index 1a31a8bcb..176c8a8a3 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "mapnode.h"
 #include "map.h"
-//#include "serverobject.h"
 #include "content_sao.h"
 #include "nodedef.h"
 #include "content_mapnode.h" // For content_mapnode_get_new_name
@@ -38,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include "util/serialize.h"
 #include "filesys.h"
+#include "log.h"
 
 
 FlagDesc flagdesc_mapgen[] = {
diff --git a/src/mapgen.h b/src/mapgen.h
index 3c897e023..b272b5cb2 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -106,9 +106,9 @@ struct MapgenSpecificParams {
 
 struct MapgenParams {
 	std::string mg_name;
-	int chunksize;
+	s16 chunksize;
 	u64 seed;
-	int water_level;
+	s16 water_level;
 	u32 flags;
 
 	MapgenSpecificParams *sparams;
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index 93751c70e..f14311fc3 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -29,7 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nameidmapping.h"
 #include "util/numeric.h"
 #include "util/serialize.h"
-//#include "profiler.h" // For TimeTaker
+#include "exceptions.h"
+#include "debug.h"
 
 /*
 	NodeBox
diff --git a/src/player.cpp b/src/player.cpp
index 40d403952..8e5f56199 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -18,12 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "player.h"
+
+#include <fstream>
+#include "util/numeric.h"
 #include "hud.h"
 #include "constants.h"
 #include "gamedef.h"
 #include "settings.h"
 #include "content_sao.h"
-#include "util/numeric.h"
+#include "filesys.h"
+#include "log.h"
 
 Player::Player(IGameDef *gamedef):
 	touching_ground(false),
@@ -195,18 +199,10 @@ void Player::serialize(std::ostream &os)
 void Player::deSerialize(std::istream &is, std::string playername)
 {
 	Settings args;
-	
-	for(;;)
-	{
-		if(is.eof())
-			throw SerializationError
-					(("Player::deSerialize(): PlayerArgsEnd of player \"" + playername + "\" not found").c_str());
-		std::string line;
-		std::getline(is, line);
-		std::string trimmedline = trim(line);
-		if(trimmedline == "PlayerArgsEnd")
-			break;
-		args.parseConfigLine(line);
+
+	if (!args.parseConfigLines(is, "PlayerArgsEnd")) {
+		throw SerializationError("PlayerArgsEnd of player " +
+				playername + " not found!");
 	}
 
 	//args.getS32("version"); // Version field value not used
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index c6d41050b..d31e84b3d 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -22,12 +22,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_vmanip.h"
 #include "common/c_converter.h"
 #include "common/c_content.h"
+#include "util/serialize.h"
 #include "server.h"
 #include "environment.h"
 #include "biome.h"
 #include "emerge.h"
 #include "mapgen_v7.h"
 #include "main.h"
+#include "log.h"
 
 
 struct EnumString ModApiMapgen::es_BiomeTerrainType[] =
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index dda5b5abf..eb6c1835d 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -23,13 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_content.h"
 #include "cpp_api/s_async.h"
 #include "serialization.h"
+#include "json/json.h"
 #include "debug.h"
 #include "porting.h"
 #include "log.h"
 #include "tool.h"
+#include "filesys.h"
 #include "settings.h"
 #include "main.h"  //required for g_settings, g_settings_path
-#include "json/json.h"
 
 // debug(...)
 // Writes a line to dstream
diff --git a/src/settings.cpp b/src/settings.cpp
new file mode 100644
index 000000000..760f07e21
--- /dev/null
+++ b/src/settings.cpp
@@ -0,0 +1,702 @@
+/*
+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 "jthread/jmutexautolock.h"
+#include "strfnd.h"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include "debug.h"
+#include "log.h"
+#include "util/serialize.h"
+#include "filesys.h"
+#include <cctype>
+
+
+Settings & Settings::operator += (const Settings &other)
+{
+	update(other);
+
+	return *this;
+}
+
+
+Settings & Settings::operator = (const Settings &other)
+{
+	if (&other == this)
+		return *this;
+
+	JMutexAutoLock lock(m_mutex);
+	JMutexAutoLock lock2(other.m_mutex);
+
+	clearNoLock();
+	updateNoLock(other);
+
+	return *this;
+}
+
+
+bool Settings::parseConfigLines(std::istream &is,
+		const std::string &end)
+{
+	JMutexAutoLock lock(m_mutex);
+
+	std::string name, value;
+	bool end_found = false;
+
+	while (is.good() && !end_found) {
+		if (parseConfigObject(is, name, value, end, end_found)) {
+			m_settings[name] = value;
+		}
+	}
+	if (!end.empty() && !end_found) {
+		return false;
+	}
+	return true;
+}
+
+
+bool Settings::readConfigFile(const char *filename)
+{
+	std::ifstream is(filename);
+	if (!is.good())
+		return false;
+
+	JMutexAutoLock lock(m_mutex);
+
+	std::string name, value;
+
+	while (is.good()) {
+		if (parseConfigObject(is, name, value)) {
+			m_settings[name] = value;
+		}
+	}
+
+	return true;
+}
+
+
+void Settings::writeLines(std::ostream &os) const
+{
+	JMutexAutoLock lock(m_mutex);
+
+	for (std::map<std::string, std::string>::const_iterator
+			i = m_settings.begin();
+			i != m_settings.end(); ++i) {
+		os << i->first << " = " << i->second << '\n';
+	}
+}
+
+
+bool Settings::updateConfigFile(const char *filename)
+{
+	std::list<std::string> objects;
+	std::set<std::string> updated;
+	bool changed = false;
+
+	JMutexAutoLock lock(m_mutex);
+
+	// Read the file and check for differences
+	{
+		std::ifstream is(filename);
+		while (is.good()) {
+			getUpdatedConfigObject(is, objects,
+					updated, changed);
+		}
+	}
+
+	// If something not yet determined to have been changed, check if
+	// any new stuff was added
+	if (!changed) {
+		for (std::map<std::string, std::string>::const_iterator
+				i = m_settings.begin();
+				i != m_settings.end(); ++i) {
+			if (updated.find(i->first) == updated.end()) {
+				changed = true;
+				break;
+			}
+		}
+	}
+
+	// If nothing was actually changed, skip writing the file
+	if (!changed) {
+		return true;
+	}
+
+	// Write stuff back
+	{
+		std::ostringstream ss(std::ios_base::binary);
+
+		// Write changes settings
+		for (std::list<std::string>::const_iterator
+				i = objects.begin();
+				i != objects.end(); ++i) {
+			ss << (*i);
+		}
+
+		// Write new settings
+		for (std::map<std::string, std::string>::const_iterator
+				i = m_settings.begin();
+				i != m_settings.end(); ++i) {
+			if (updated.find(i->first) != updated.end())
+				continue;
+			ss << i->first << " = " << i->second << '\n';
+		}
+
+		if (!fs::safeWriteToFile(filename, ss.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 *
+ ***********/
+
+
+std::string Settings::get(const std::string &name) const
+{
+	JMutexAutoLock lock(m_mutex);
+
+	std::map<std::string, std::string>::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;
+}
+
+
+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::exists(const std::string &name) const
+{
+	JMutexAutoLock 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 (std::map<std::string, std::string>::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::getNoEx(const std::string &name, std::string &val) const
+{
+	try {
+		val = get(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 *
+ ***********/
+
+
+void Settings::set(const std::string &name, std::string value)
+{
+	JMutexAutoLock lock(m_mutex);
+
+	m_settings[name] = value;
+}
+
+
+void Settings::set(const std::string &name, const char *value)
+{
+	JMutexAutoLock lock(m_mutex);
+
+	m_settings[name] = value;
+}
+
+
+void Settings::setDefault(const std::string &name, std::string value)
+{
+	JMutexAutoLock lock(m_mutex);
+
+	m_defaults[name] = value;
+}
+
+
+void Settings::setBool(const std::string &name, bool value)
+{
+	set(name, value ? "true" : "false");
+}
+
+
+void Settings::setS16(const std::string &name, s16 value)
+{
+	set(name, itos(value));
+}
+
+
+void Settings::setS32(const std::string &name, s32 value)
+{
+	set(name, itos(value));
+}
+
+
+void Settings::setU64(const std::string &name, u64 value)
+{
+	std::ostringstream os;
+	os << value;
+	set(name, os.str());
+}
+
+
+void Settings::setFloat(const std::string &name, float value)
+{
+	set(name, ftos(value));
+}
+
+
+void Settings::setV2F(const std::string &name, v2f value)
+{
+	std::ostringstream os;
+	os << "(" << value.X << "," << value.Y << ")";
+	set(name, os.str());
+}
+
+
+void Settings::setV3F(const std::string &name, v3f value)
+{
+	std::ostringstream os;
+	os << "(" << value.X << "," << value.Y << "," << value.Z << ")";
+	set(name, os.str());
+}
+
+
+void Settings::setFlagStr(const std::string &name, u32 flags,
+	const FlagDesc *flagdesc, u32 flagmask)
+{
+	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;
+
+	set(name, structstr);
+	return true;
+}
+
+
+bool Settings::remove(const std::string &name)
+{
+	JMutexAutoLock lock(m_mutex);
+	return m_settings.erase(name);
+}
+
+
+void Settings::clear()
+{
+	JMutexAutoLock lock(m_mutex);
+	clearNoLock();
+}
+
+
+void Settings::updateValue(const Settings &other, const std::string &name)
+{
+	if (&other == this)
+		return;
+
+	JMutexAutoLock 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;
+
+	JMutexAutoLock lock(m_mutex);
+	JMutexAutoLock lock2(other.m_mutex);
+
+	updateNoLock(other);
+}
+
+
+inline bool Settings::parseConfigObject(std::istream &is,
+		std::string &name, std::string &value)
+{
+	bool end_found = false;
+	return parseConfigObject(is, name, value, "", end_found);
+}
+
+
+// NOTE: This function might be expanded to allow multi-line settings.
+bool Settings::parseConfigObject(std::istream &is,
+		std::string &name, std::string &value,
+		const std::string &end, bool &end_found)
+{
+	std::string line;
+	std::getline(is, line);
+	std::string trimmed_line = trim(line);
+
+	// Ignore empty lines and comments
+	if (trimmed_line.empty() || trimmed_line[0] == '#') {
+		value = trimmed_line;
+		return false;
+	}
+	if (trimmed_line == end) {
+		end_found = true;
+		return false;
+	}
+
+	Strfnd sf(trimmed_line);
+
+	name = trim(sf.next("="));
+	if (name.empty()) {
+		value = trimmed_line;
+		return false;
+	}
+
+	value = trim(sf.next("\n"));
+
+	return true;
+}
+
+
+void Settings::getUpdatedConfigObject(std::istream &is,
+		std::list<std::string> &dst,
+		std::set<std::string> &updated,
+		bool &changed)
+{
+	std::string name, value;
+	if (!parseConfigObject(is, name, value)) {
+		dst.push_back(value + '\n');
+		return;
+	}
+
+	if (m_settings.find(name) != m_settings.end()) {
+		std::string new_value = m_settings[name];
+
+		if (new_value != value) {
+			changed = true;
+		}
+
+		dst.push_back(name + " = " + new_value + '\n');
+		updated.insert(name);
+	} else { // File contains a setting which is not in m_settings
+		changed = true;
+	}
+}
+
+
+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()
+{
+	m_settings.clear();
+	m_defaults.clear();
+}
+
diff --git a/src/settings.h b/src/settings.h
index bfe7dd5a4..f0ef9f6b2 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -21,23 +21,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define SETTINGS_HEADER
 
 #include "irrlichttypes_bloated.h"
-#include "exceptions.h"
-#include <string>
-#include "jthread/jmutex.h"
-#include "jthread/jmutexautolock.h"
-#include "strfnd.h"
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include "debug.h"
-#include "log.h"
 #include "util/string.h"
-#include "util/serialize.h"
-#include <list>
+#include "jthread/jmutex.h"
+#include <string>
 #include <map>
+#include <list>
 #include <set>
-#include "filesys.h"
-#include <cctype>
 
 enum ValueType
 {
@@ -56,753 +45,126 @@ struct ValueSpec
 	const char *help;
 };
 
+
 class Settings
 {
 public:
-	Settings()
-	{
-	}
-
-	void writeLines(std::ostream &os) const
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		for(std::map<std::string, std::string>::const_iterator
-				i = m_settings.begin();
-				i != m_settings.end(); ++i)
-		{
-			std::string name = i->first;
-			std::string value = i->second;
-			os<<name<<" = "<<value<<"\n";
-		}
-	}
-  
-	// return all keys used
-	std::vector<std::string> getNames() const
-	{
-		std::vector<std::string> names;
-		for(std::map<std::string, std::string>::const_iterator
-				i = m_settings.begin();
-				i != m_settings.end(); ++i)
-		{
-			names.push_back(i->first);
-		}
-		return names;
-	}
-
-	// remove a setting
-	bool remove(const std::string &name)
-	{
-		return m_settings.erase(name);
-	}
-
-
-	bool parseConfigLine(const std::string &line)
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		std::string trimmedline = trim(line);
-
-		// Ignore empty lines and comments
-		if(trimmedline.size() == 0 || trimmedline[0] == '#')
-			return true;
-
-		//infostream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
-
-		Strfnd sf(trim(line));
-
-		std::string name = sf.next("=");
-		name = trim(name);
-
-		if(name == "")
-			return true;
-
-		std::string value = sf.next("\n");
-		value = trim(value);
-
-		/*infostream<<"Config name=\""<<name<<"\" value=\""
-				<<value<<"\""<<std::endl;*/
-
-		m_settings[name] = value;
-
-		return true;
-	}
-
-	void parseConfigLines(std::istream &is, const std::string &endstring)
-	{
-		for(;;){
-			if(is.eof())
-				break;
-			std::string line;
-			std::getline(is, line);
-			std::string trimmedline = trim(line);
-			if(endstring != ""){
-				if(trimmedline == endstring)
-					break;
-			}
-			parseConfigLine(line);
-		}
-	}
-
-	// Returns false on EOF
-	bool parseConfigObject(std::istream &is)
-	{
-		if(is.eof())
-			return false;
-
-		/*
-			NOTE: This function might be expanded to allow multi-line
-			      settings.
-		*/
-		std::string line;
-		std::getline(is, line);
-		//infostream<<"got line: \""<<line<<"\""<<std::endl;
-
-		return parseConfigLine(line);
-	}
-
-	/*
-		Read configuration file
-
-		Returns true on success
-	*/
-	bool readConfigFile(const char *filename)
-	{
-		std::ifstream is(filename);
-		if(is.good() == false)
-			return false;
-
-		/*infostream<<"Parsing configuration file: \""
-				<<filename<<"\""<<std::endl;*/
-
-		while(parseConfigObject(is));
-
-		return true;
-	}
-
-	/*
-		Reads a configuration object from stream (usually a single line)
-		and adds it to dst.
-
-		Preserves comments and empty lines.
-
-		Settings that were added to dst are also added to updated.
-		key of updated is setting name, value of updated is dummy.
-
-		Returns false on EOF
-	*/
-	bool getUpdatedConfigObject(std::istream &is,
-			std::list<std::string> &dst,
-			std::set<std::string> &updated,
-			bool &value_changed)
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		if(is.eof())
-			return false;
-
-		// NOTE: This function will be expanded to allow multi-line settings
-		std::string line;
-		std::getline(is, line);
-
-		std::string trimmedline = trim(line);
-
-		std::string line_end = "";
-		if(is.eof() == false)
-			line_end = "\n";
-
-		// Ignore empty lines and comments
-		if(trimmedline.size() == 0 || trimmedline[0] == '#')
-		{
-			dst.push_back(line+line_end);
-			return true;
-		}
-
-		Strfnd sf(trim(line));
-
-		std::string name = sf.next("=");
-		name = trim(name);
-
-		if(name == "")
-		{
-			dst.push_back(line+line_end);
-			return true;
-		}
-
-		std::string value = sf.next("\n");
-		value = trim(value);
-
-		if(m_settings.find(name) != m_settings.end())
-		{
-			std::string newvalue = m_settings[name];
-
-			if(newvalue != value)
-			{
-				infostream<<"Changing value of \""<<name<<"\" = \""
-						<<value<<"\" -> \""<<newvalue<<"\""
-						<<std::endl;
-				value_changed = true;
-			}
-
-			dst.push_back(name + " = " + newvalue + line_end);
-
-			updated.insert(name);
-		}
-		else //file contains a setting which is not in m_settings
-			value_changed=true;
-			
-		return true;
-	}
+	Settings() {}
 
-	/*
-		Updates configuration file
+	Settings & operator += (const Settings &other);
+	Settings & operator = (const Settings &other);
 
-		Returns true on success
-	*/
-	bool updateConfigFile(const char *filename)
-	{
-		infostream<<"Updating configuration file: \""
-				<<filename<<"\""<<std::endl;
-
-		std::list<std::string> objects;
-		std::set<std::string> updated;
-		bool something_actually_changed = false;
-
-		// Read and modify stuff
-		{
-			std::ifstream is(filename);
-			if(is.good() == false)
-			{
-				infostream<<"updateConfigFile():"
-						" Error opening configuration file"
-						" for reading: \""
-						<<filename<<"\""<<std::endl;
-			}
-			else
-			{
-				while(getUpdatedConfigObject(is, objects, updated,
-						something_actually_changed));
-			}
-		}
-
-		JMutexAutoLock lock(m_mutex);
-
-		// If something not yet determined to have been changed, check if
-		// any new stuff was added
-		if(!something_actually_changed){
-			for(std::map<std::string, std::string>::const_iterator
-					i = m_settings.begin();
-					i != m_settings.end(); ++i)
-			{
-				if(updated.find(i->first) != updated.end())
-					continue;
-				something_actually_changed = true;
-				break;
-			}
-		}
-
-		// If nothing was actually changed, skip writing the file
-		if(!something_actually_changed){
-			infostream<<"Skipping writing of "<<filename
-					<<" because content wouldn't be modified"<<std::endl;
-			return true;
-		}
-
-		// Write stuff back
-		{
-			std::ostringstream ss(std::ios_base::binary);
-
-			/*
-				Write updated stuff
-			*/
-			for(std::list<std::string>::const_iterator
-					i = objects.begin();
-					i != objects.end(); ++i)
-			{
-				ss<<(*i);
-			}
-
-			/*
-				Write stuff that was not already in the file
-			*/
-			for(std::map<std::string, std::string>::const_iterator
-					i = m_settings.begin();
-					i != m_settings.end(); ++i)
-			{
-				if(updated.find(i->first) != updated.end())
-					continue;
-				std::string name = i->first;
-				std::string value = i->second;
-				infostream<<"Adding \""<<name<<"\" = \""<<value<<"\""
-						<<std::endl;
-				ss<<name<<" = "<<value<<"\n";
-			}
-
-			if(!fs::safeWriteToFile(filename, ss.str()))
-			{
-				errorstream<<"Error writing configuration file: \""
-						<<filename<<"\""<<std::endl;
-				return false;
-			}
-		}
-
-		return true;
-	}
 
-	/*
-		NOTE: Types of allowed_options are ignored
+	/***********************
+	 * Reading and writing *
+	 ***********************/
 
-		returns true on success
-	*/
+	// 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)
-	{
-		int nonopt_index = 0;
-		int i=1;
-		for(;;)
-		{
-			if(i >= argc)
-				break;
-			std::string argname = argv[i];
-			if(argname.substr(0, 2) != "--")
-			{
-				// If option doesn't start with -, read it in as nonoptX
-				if(argname[0] != '-'){
-					std::string name = "nonopt";
-					name += itos(nonopt_index);
-					set(name, argname);
-					nonopt_index++;
-					i++;
-					continue;
-				}
-				errorstream<<"Invalid command-line parameter \""
-						<<argname<<"\": --<option> expected."<<std::endl;
-				return false;
-			}
-			i++;
-
-			std::string name = argname.substr(2);
-
-			std::map<std::string, ValueSpec>::iterator n;
-			n = allowed_options.find(name);
-			if(n == allowed_options.end())
-			{
-				errorstream<<"Unknown command-line parameter \""
-						<<argname<<"\""<<std::endl;
-				return false;
-			}
-
-			ValueType type = n->second.type;
-
-			std::string value = "";
-
-			if(type == VALUETYPE_FLAG)
-			{
-				value = "true";
-			}
-			else
-			{
-				if(i >= argc)
-				{
-					errorstream<<"Invalid command-line parameter \""
-							<<name<<"\": missing value"<<std::endl;
-					return false;
-				}
-				value = argv[i];
-				i++;
-			}
-
-
-			infostream<<"Valid command-line parameter: \""
-					<<name<<"\" = \""<<value<<"\""
-					<<std::endl;
-			set(name, value);
-		}
-
-		return true;
-	}
-
-	void set(const std::string &name, std::string value)
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		m_settings[name] = value;
-	}
-
-	void set(const std::string &name, const char *value)
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		m_settings[name] = value;
-	}
-
-
-	void setDefault(const std::string &name, std::string value)
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		m_defaults[name] = value;
-	}
-
-	bool exists(const std::string &name) const
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		return (m_settings.find(name) != m_settings.end() ||
-			m_defaults.find(name) != m_defaults.end());
-	}
-
-	std::string get(const std::string &name) const
-	{
-		JMutexAutoLock lock(m_mutex);
-
-		std::map<std::string, std::string>::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 ").c_str());
-
-		return n->second;
-	}
-
-	//////////// Get setting
-	bool getBool(const std::string &name) const
-	{
-		return is_yes(get(name));
-	}
-
-	bool getFlag(const std::string &name) const
-	{
-		try {
-			return getBool(name);
-		} catch(SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	float getFloat(const std::string &name) const
-	{
-		return stof(get(name));
-	}
-
-	u16 getU16(const std::string &name) const
-	{
-		return stoi(get(name), 0, 65535);
-	}
-
-	s16 getS16(const std::string &name) const
-	{
-		return stoi(get(name), -32768, 32767);
-	}
-
-	s32 getS32(const std::string &name) const
-	{
-		return stoi(get(name));
-	}
-
-	v3f 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;
-	}
-
-	v2f 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;
-	}
-
-	u64 getU64(const std::string &name) const
-	{
-		u64 value = 0;
-		std::string s = get(name);
-		std::istringstream ss(s);
-		ss >> value;
-		return value;
-	}
-
+			std::map<std::string, ValueSpec> &allowed_options);
+	bool parseConfigLines(std::istream &is, const std::string &end = "");
+	void writeLines(std::ostream &os) const;
+
+
+	/***********
+	 * Getters *
+	 ***********/
+
+	std::string get(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
-	{
-		std::string val = get(name);
-		return std::isdigit(val[0])
-			? stoi(val)
-			: readFlagString(val, flagdesc, flagmask);
-	}
-
+			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
-	{
-		std::string valstr;
-
-		try {
-			valstr = get(name);
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-
-		if (!deSerializeStringToStruct(valstr, format, out, olen))
-			return false;
-
-		return true;
-	}
-
-	//////////// Try to get value, no exception thrown
-	bool getNoEx(const std::string &name, std::string &val) const
-	{
-		try {
-			val = get(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
+			void *out, size_t olen) 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 getNoEx(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
-	{
-		try {
-			u32 flags, flagmask;
-
-			flags = getFlagStr(name, flagdesc, &flagmask);
-
-			val &= ~flagmask;
-			val |=  flags;
-
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getFloatNoEx(const std::string &name, float &val) const
-	{
-		try {
-			val = getFloat(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getU16NoEx(const std::string &name, int &val) const
-	{
-		try {
-			val = getU16(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getU16NoEx(const std::string &name, u16 &val) const
-	{
-		try {
-			val = getU16(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getS16NoEx(const std::string &name, int &val) const
-	{
-		try {
-			val = getU16(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getS16NoEx(const std::string &name, s16 &val) const
-	{
-		try {
-			val = getS16(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getS32NoEx(const std::string &name, s32 &val) const
-	{
-		try {
-			val = getS32(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getV3FNoEx(const std::string &name, v3f &val) const
-	{
-		try {
-			val = getV3F(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getV2FNoEx(const std::string &name, v2f &val) const
-	{
-		try {
-			val = getV2F(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	bool getU64NoEx(const std::string &name, u64 &val) const
-	{
-		try {
-			val = getU64(name);
-			return true;
-		} catch (SettingNotFoundException &e) {
-			return false;
-		}
-	}
-
-	//////////// Set setting
-
+	bool getFlagStrNoEx(const std::string &name, u32 &val, FlagDesc *flagdesc) const;
+
+
+	/***********
+	 * Setters *
+	 ***********/
+
+	void set(const std::string &name, std::string value);
+	void set(const std::string &name, const char *value);
+	void setDefault(const std::string &name, std::string value);
+	void setBool(const std::string &name, bool value);
+	void setS16(const std::string &name, s16 value);
+	void setS32(const std::string &name, s32 value);
+	void setU64(const std::string &name, u64 value);
+	void setFloat(const std::string &name, float value);
+	void setV2F(const std::string &name, v2f value);
+	void setV3F(const std::string &name, v3f value);
+	void setFlagStr(const std::string &name, u32 flags,
+		const FlagDesc *flagdesc, u32 flagmask);
 	// 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)
-	{
-		std::string structstr;
-		if (!serializeStructToString(&structstr, format, value))
-			return false;
-
-		set(name, structstr);
-		return true;
-	}
+	bool setStruct(const std::string &name, const std::string &format, void *value);
 
-	void setFlagStr(const std::string &name, u32 flags,
-		const FlagDesc *flagdesc, u32 flagmask)
-	{
-		set(name, writeFlagString(flags, flagdesc, flagmask));
-	}
-
-	void setBool(const std::string &name, bool value)
-	{
-		set(name, value ? "true" : "false");
-	}
-
-	void setFloat(const std::string &name, float value)
-	{
-		set(name, ftos(value));
-	}
-
-	void setV3F(const std::string &name, v3f value)
-	{
-		std::ostringstream os;
-		os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
-		set(name, os.str());
-	}
-
-	void setV2F(const std::string &name, v2f value)
-	{
-		std::ostringstream os;
-		os<<"("<<value.X<<","<<value.Y<<")";
-		set(name, os.str());
-	}
-
-	void setS16(const std::string &name, s16 value)
-	{
-		set(name, itos(value));
-	}
-
-	void setS32(const std::string &name, s32 value)
-	{
-		set(name, itos(value));
-	}
-
-	void setU64(const std::string &name, u64 value)
-	{
-		std::ostringstream os;
-		os<<value;
-		set(name, os.str());
-	}
-
-	void clear()
-	{
-		JMutexAutoLock lock(m_mutex);
-		clearNoLock();
-	}
-
-	void updateValue(const Settings &other, const std::string &name)
-	{
-		if (&other == this)
-			return;
-
-		JMutexAutoLock lock(m_mutex);
-
-		try {
-			std::string val = other.get(name);
-			m_settings[name] = val;
-		} catch (SettingNotFoundException &e) {
-		}
-	}
-
-	void update(const Settings &other)
-	{
-		if (&other == this)
-			return;
-
-		JMutexAutoLock lock(m_mutex);
-		JMutexAutoLock lock2(other.m_mutex);
-
-		updateNoLock(other);
-	}
-
-	Settings & operator+=(const Settings &other)
-	{
-		update(other);
-
-		return *this;
-	}
-
-	Settings & operator=(const Settings &other)
-	{
-		if (&other == this)
-			return *this;
-
-		JMutexAutoLock lock(m_mutex);
-		JMutexAutoLock lock2(other.m_mutex);
-
-		clearNoLock();
-		updateNoLock(other);
+	// remove a setting
+	bool remove(const std::string &name);
+	void clear();
+	void updateValue(const Settings &other, const std::string &name);
+	void update(const Settings &other);
 
-		return *this;
-	}
 
 private:
+	/***********************
+	 * Reading and writing *
+	 ***********************/
+
+	bool parseConfigObject(std::istream &is,
+			std::string &name, std::string &value);
+	bool parseConfigObject(std::istream &is,
+			std::string &name, std::string &value,
+			const std::string &end, bool &end_found);
+	/*
+	 * Reads a configuration object from stream (usually a single line)
+	 * and adds it to dst.
+	 * Preserves comments and empty lines.
+	 * Setting names that were added to dst are also added to updated.
+	 */
+	void getUpdatedConfigObject(std::istream &is,
+			std::list<std::string> &dst,
+			std::set<std::string> &updated,
+			bool &changed);
 
-	void 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 clearNoLock()
-	{
-		m_settings.clear();
-		m_defaults.clear();
-	}
+	void updateNoLock(const Settings &other);
+	void clearNoLock();
 
 
 	std::map<std::string, std::string> m_settings;
diff --git a/src/socket.cpp b/src/socket.cpp
index 508d61a31..0e9183f18 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -19,6 +19,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "socket.h"
 
+#include <stdio.h>
+#include <iostream>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sstream>
+#include <iomanip>
+#include "util/string.h"
+#include "util/numeric.h"
+#include "constants.h"
+#include "debug.h"
+#include "settings.h"
+#include "log.h"
+#include "main.h" // for g_settings
+
 #ifdef _WIN32
 	#ifndef WIN32_LEAN_AND_MEAN
 		#define WIN32_LEAN_AND_MEAN
@@ -46,20 +61,6 @@ typedef int socklen_t;
 typedef int socket_t;
 #endif
 
-#include "constants.h"
-#include "debug.h"
-#include "settings.h"
-#include "main.h" // for g_settings
-#include <stdio.h>
-#include <iostream>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sstream>
-#include <iomanip>
-#include "util/string.h"
-#include "util/numeric.h"
-
 // Set to true to enable verbose debug output
 bool socket_enable_debug_output = false;
 
diff --git a/src/test.cpp b/src/test.cpp
index 8a080454b..956397f26 100644
--- a/src/test.cpp
+++ b/src/test.cpp
@@ -390,12 +390,14 @@ struct TestSettings: public TestBase
 	{
 		Settings s;
 		// Test reading of settings
-		s.parseConfigLine("leet = 1337");
-		s.parseConfigLine("leetleet = 13371337");
-		s.parseConfigLine("leetleet_neg = -13371337");
-		s.parseConfigLine("floaty_thing = 1.1");
-		s.parseConfigLine("stringy_thing = asd /( ¤%&(/\" BLÖÄRP");
-		s.parseConfigLine("coord = (1, 2, 4.5)");
+		std::istringstream is(
+			"leet = 1337\n"
+			"leetleet = 13371337\n"
+			"leetleet_neg = -13371337\n"
+			"floaty_thing = 1.1\n"
+			"stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n"
+			"coord = (1, 2, 4.5)");
+		s.parseConfigLines(is);
 		UASSERT(s.getS32("leet") == 1337);
 		UASSERT(s.getS16("leetleet") == 32767);
 		UASSERT(s.getS16("leetleet_neg") == -32768);
diff --git a/src/tile.cpp b/src/tile.cpp
index 06d89393c..ebef77fb9 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -18,19 +18,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "tile.h"
+
+#include <ICameraSceneNode.h>
+#include "util/string.h"
+#include "util/container.h"
+#include "util/thread.h"
+#include "util/numeric.h"
 #include "irrlichttypes_extrabloated.h"
 #include "debug.h"
 #include "main.h" // for g_settings
 #include "filesys.h"
 #include "settings.h"
 #include "mesh.h"
-#include <ICameraSceneNode.h>
 #include "log.h"
 #include "gamedef.h"
-#include "util/string.h"
-#include "util/container.h"
-#include "util/thread.h"
-#include "util/numeric.h"
+#include "strfnd.h"
 
 #ifdef __ANDROID__
 #include <GLES/gl.h>
-- 
GitLab