diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index 05ef3ca0e..55ed29ceb 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -47,11 +47,6 @@ gui::IGUIEnvironment *guienv = nullptr;
 gui::IGUIStaticText *guiroot = nullptr;
 MainMenuManager g_menumgr;
 
-bool isMenuActive()
-{
-	return g_menumgr.menuCount() != 0;
-}
-
 // Passed to menus to allow disconnecting and exiting
 MainGameCallback *g_gamecallback = nullptr;
 
@@ -74,13 +69,20 @@ ClientLauncher::~ClientLauncher()
 {
 	delete input;
 
-	delete receiver;
-
 	delete g_fontengine;
+	g_fontengine = nullptr;
 	delete g_gamecallback;
+	g_gamecallback = nullptr;
+
+	guiroot = nullptr;
+	guienv = nullptr;
+	assert(g_menumgr.menuCount() == 0);
 
 	delete m_rendering_engine;
 
+	// delete event receiver only after all Irrlicht stuff is gone
+	delete receiver;
+
 #if USE_SOUND
 	g_sound_manager_singleton.reset();
 #endif
@@ -103,10 +105,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 		g_sound_manager_singleton = createSoundManagerSingleton();
 #endif
 
-	if (!init_engine()) {
-		errorstream << "Could not initialize game engine." << std::endl;
+	if (!init_engine())
 		return false;
-	}
 
 	if (!m_rendering_engine->get_video_driver()) {
 		errorstream << "Could not initialize video driver." << std::endl;
@@ -129,7 +129,6 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
 	init_guienv(guienv);
 
 	g_fontengine = new FontEngine(guienv);
-	FATAL_ERROR_IF(!g_fontengine, "Font engine creation failed.");
 
 	// Create the menu clouds
 	// This is only global so it can be used by RenderingEngine::draw_load_screen().
@@ -301,8 +300,12 @@ void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_ar
 bool ClientLauncher::init_engine()
 {
 	receiver = new MyEventReceiver();
-	m_rendering_engine = new RenderingEngine(receiver);
-	return m_rendering_engine->get_raw_device() != nullptr;
+	try {
+		m_rendering_engine = new RenderingEngine(receiver);
+	} catch (std::exception &e) {
+		errorstream << e.what() << std::endl;
+	}
+	return !!m_rendering_engine;
 }
 
 void ClientLauncher::init_input()
diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp
index 9fb110fdc..945f41e89 100644
--- a/src/client/content_cao.cpp
+++ b/src/client/content_cao.cpp
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "camera.h" // CameraModes
 #include "collision.h"
 #include "content_cso.h"
+#include "clientobject.h"
 #include "environment.h"
 #include "itemdef.h"
 #include "localplayer.h"
@@ -218,14 +219,15 @@ private:
 };
 
 // Prototype
-TestCAO proto_TestCAO(NULL, NULL);
+static TestCAO proto_TestCAO(nullptr, nullptr);
 
 TestCAO::TestCAO(Client *client, ClientEnvironment *env):
 	ClientActiveObject(0, client, env),
 	m_node(NULL),
 	m_position(v3f(0,10*BS,0))
 {
-	ClientActiveObject::registerType(getType(), create);
+	if (!client)
+		ClientActiveObject::registerType(getType(), create);
 }
 
 std::unique_ptr<ClientActiveObject> TestCAO::create(Client *client, ClientEnvironment *env)
@@ -322,8 +324,6 @@ void TestCAO::processMessage(const std::string &data)
 	GenericCAO
 */
 
-#include "clientobject.h"
-
 GenericCAO::GenericCAO(Client *client, ClientEnvironment *env):
 		ClientActiveObject(0, client, env)
 {
@@ -2082,4 +2082,4 @@ void GenericCAO::updateMeshCulling()
 }
 
 // Prototype
-GenericCAO proto_GenericCAO(NULL, NULL);
+static GenericCAO proto_GenericCAO(nullptr, nullptr);
diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp
index 0ae50dfe2..0b1b0928e 100644
--- a/src/client/fontengine.cpp
+++ b/src/client/fontengine.cpp
@@ -27,16 +27,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlicht_changes/CGUITTFont.h"
 #include "util/numeric.h" // rangelim
 
-/** maximum size distance for getting a "similar" font size */
-#define MAX_FONT_SIZE_OFFSET 10
-
 /** reference to access font engine, has to be initialized by main */
-FontEngine* g_fontengine = NULL;
+FontEngine *g_fontengine = nullptr;
 
 /** callback to be used on change of font size setting */
 static void font_setting_changed(const std::string &name, void *userdata)
 {
-	g_fontengine->readSettings();
+	if (g_fontengine)
+		g_fontengine->readSettings();
 }
 
 /******************************************************************************/
@@ -226,7 +224,7 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
 	u16 divisible_by = g_settings->getU16(setting_prefix + "font_size_divisible_by");
 	if (divisible_by > 1) {
 		size = std::max<u32>(
-				std::round((double)size / divisible_by) * divisible_by, divisible_by);
+				std::round((float)size / divisible_by) * divisible_by, divisible_by);
 	}
 
 	sanity_check(size != 0);
diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp
index fcfae5022..b7b9af0ff 100644
--- a/src/client/guiscalingfilter.cpp
+++ b/src/client/guiscalingfilter.cpp
@@ -29,12 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
  * converting textures back into images repeatedly, and some don't even
  * allow it at all.
  */
-std::map<io::path, video::IImage *> g_imgCache;
+static std::map<io::path, video::IImage *> g_imgCache;
 
 /* Maintain a static cache of all pre-scaled textures.  These need to be
  * cleared as well when the cached images.
  */
-std::map<io::path, video::ITexture *> g_txrCache;
+static std::map<io::path, video::ITexture *> g_txrCache;
 
 /* Manually insert an image into the cache, useful to avoid texture-to-image
  * conversion whenever we can intercept it.
diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp
index 14360b1a1..1f960f71f 100644
--- a/src/client/keycode.cpp
+++ b/src/client/keycode.cpp
@@ -355,12 +355,11 @@ const KeyPress CancelKey("KEY_CANCEL");
 */
 
 // A simple cache for quicker lookup
-std::unordered_map<std::string, KeyPress> g_key_setting_cache;
+static std::unordered_map<std::string, KeyPress> g_key_setting_cache;
 
 KeyPress getKeySetting(const char *settingname)
 {
-	std::unordered_map<std::string, KeyPress>::iterator n;
-	n = g_key_setting_cache.find(settingname);
+	auto n = g_key_setting_cache.find(settingname);
 	if (n != g_key_setting_cache.end())
 		return n->second;
 
diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp
index b0d95bd90..3dbb39f86 100644
--- a/src/client/renderingengine.cpp
+++ b/src/client/renderingengine.cpp
@@ -253,8 +253,11 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
 
 RenderingEngine::~RenderingEngine()
 {
+	sanity_check(s_singleton == this);
+
 	core.reset();
 	m_device->closeDevice();
+	m_device->drop();
 	s_singleton = nullptr;
 }
 
@@ -278,10 +281,7 @@ void RenderingEngine::removeMesh(const scene::IMesh* mesh)
 void RenderingEngine::cleanupMeshCache()
 {
 	auto mesh_cache = m_device->getSceneManager()->getMeshCache();
-	while (mesh_cache->getMeshCount() != 0) {
-		if (scene::IAnimatedMesh *mesh = mesh_cache->getMeshByIndex(0))
-			mesh_cache->removeMesh(mesh);
-	}
+	mesh_cache->clear();
 }
 
 bool RenderingEngine::setupTopLevelWindow()
diff --git a/src/client/shader.cpp b/src/client/shader.cpp
index 4f84ecbe7..3c7aeac39 100644
--- a/src/client/shader.cpp
+++ b/src/client/shader.cpp
@@ -46,7 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 /*
 	A cache from shader name to shader path
 */
-MutexedMap<std::string, std::string> g_shadername_to_path_cache;
+static MutexedMap<std::string, std::string> g_shadername_to_path_cache;
 
 /*
 	Gets the path to a shader by first checking if the file
diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp
index 02039aa1c..66f89efb1 100644
--- a/src/client/wieldmesh.cpp
+++ b/src/client/wieldmesh.cpp
@@ -191,7 +191,7 @@ private:
 	scene::IMesh *m_cube;
 };
 
-ExtrusionMeshCache *g_extrusion_mesh_cache = NULL;
+static ExtrusionMeshCache *g_extrusion_mesh_cache = nullptr;
 
 
 WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool lighting):
diff --git a/src/emerge.h b/src/emerge.h
index 6e4223c2d..4718637ad 100644
--- a/src/emerge.h
+++ b/src/emerge.h
@@ -70,7 +70,7 @@ enum EmergeAction {
 	EMERGE_GENERATED,
 };
 
-const static std::string emergeActionStrs[] = {
+constexpr const char *emergeActionStrs[] = {
 	"cancelled",
 	"errored",
 	"from_memory",
diff --git a/src/exceptions.h b/src/exceptions.h
index 184f8ed60..efa315b96 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class BaseException : public std::exception
 {
 public:
-	BaseException(const std::string &s) throw(): m_s(s) {}
+	BaseException(const std::string &s) noexcept: m_s(s) {}
 	~BaseException() throw() = default;
 
-	virtual const char * what() const throw()
+	virtual const char * what() const noexcept
 	{
 		return m_s.c_str();
 	}
diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h
index 9ef1457cf..05d7c8974 100644
--- a/src/gui/mainmenumanager.h
+++ b/src/gui/mainmenumanager.h
@@ -80,7 +80,7 @@ public:
 		return mm && mm->preprocessEvent(event);
 	}
 
-	u32 menuCount()
+	size_t menuCount() const
 	{
 		return m_stack.size();
 	}
@@ -95,12 +95,16 @@ public:
 		return false;
 	}
 
+	// FIXME: why isn't this private?
 	std::list<gui::IGUIElement*> m_stack;
 };
 
 extern MainMenuManager g_menumgr;
 
-extern bool isMenuActive();
+static inline bool isMenuActive()
+{
+	return g_menumgr.menuCount() != 0;
+}
 
 class MainGameCallback : public IGameCallback
 {
@@ -138,7 +142,8 @@ public:
 		keyconfig_changed = true;
 	}
 
-	void showOpenURLDialog(const std::string &url) override {
+	void showOpenURLDialog(const std::string &url) override
+	{
 		show_open_url_dialog = url;
 	}
 
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index add80b3b2..4d9900ea0 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -1098,7 +1098,7 @@ enum NetProtoCompressionMode {
 	NETPROTO_COMPRESSION_NONE = 0,
 };
 
-const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
+constexpr const char *accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
 	"Invalid password",
 	"Your client sent something the server didn't expect.  Try reconnecting or updating your client.",
 	"The server is running in simple singleplayer mode.  You cannot connect.",
diff --git a/src/unittest/test_authdatabase.cpp b/src/unittest/test_authdatabase.cpp
index 1e1744d40..856f04437 100644
--- a/src/unittest/test_authdatabase.cpp
+++ b/src/unittest/test_authdatabase.cpp
@@ -115,7 +115,7 @@ static TestAuthDatabase g_test_instance;
 void TestAuthDatabase::runTests(IGameDef *gamedef)
 {
 	// fixed directory, for persistence
-	thread_local const std::string test_dir = getTestTempDirectory();
+	const std::string test_dir = getTestTempDirectory();
 
 	// Each set of tests is run twice for each database type:
 	// one where we reuse the same AuthDatabase object (to test local caching),
diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp
index f5145ee16..dbb60abdc 100644
--- a/src/unittest/test_settings.cpp
+++ b/src/unittest/test_settings.cpp
@@ -36,7 +36,7 @@ public:
 	void testFlagDesc();
 
 	static const char *config_text_before;
-	static const std::string config_text_after;
+	static const char *config_text_after;
 };
 
 static TestSettings g_test_instance;
@@ -76,7 +76,7 @@ const char *TestSettings::config_text_before =
 	"zoop = true\n"
 	"[dummy_eof_end_tag]\n";
 
-const std::string TestSettings::config_text_after =
+const char *TestSettings::config_text_after =
 	u8"leet = 1337\n"
 	"leetleet = 13371337\n"
 	"leetleet_neg = -13371337\n"
diff --git a/src/util/quicktune.cpp b/src/util/quicktune.cpp
index 37d4933de..1acd94fa6 100644
--- a/src/util/quicktune.cpp
+++ b/src/util/quicktune.cpp
@@ -31,6 +31,7 @@ std::string QuicktuneValue::getString()
 	}
 	return "<invalid type>";
 }
+
 void QuicktuneValue::relativeAdd(float amount)
 {
 	switch(type){
@@ -48,24 +49,16 @@ void QuicktuneValue::relativeAdd(float amount)
 
 static std::map<std::string, QuicktuneValue> g_values;
 static std::vector<std::string> g_names;
-std::mutex *g_mutex = NULL;
+static std::mutex g_mutex;
 
-static void makeMutex()
-{
-	if(!g_mutex){
-		g_mutex = new std::mutex();
-	}
-}
-
-std::vector<std::string> getQuicktuneNames()
+const std::vector<std::string> &getQuicktuneNames()
 {
 	return g_names;
 }
 
 QuicktuneValue getQuicktuneValue(const std::string &name)
 {
-	makeMutex();
-	MutexAutoLock lock(*g_mutex);
+	MutexAutoLock lock(g_mutex);
 	std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
 	if(i == g_values.end()){
 		QuicktuneValue val;
@@ -77,17 +70,15 @@ QuicktuneValue getQuicktuneValue(const std::string &name)
 
 void setQuicktuneValue(const std::string &name, const QuicktuneValue &val)
 {
-	makeMutex();
-	MutexAutoLock lock(*g_mutex);
+	MutexAutoLock lock(g_mutex);
 	g_values[name] = val;
 	g_values[name].modified = true;
 }
 
 void updateQuicktuneValue(const std::string &name, QuicktuneValue &val)
 {
-	makeMutex();
-	MutexAutoLock lock(*g_mutex);
-	std::map<std::string, QuicktuneValue>::iterator i = g_values.find(name);
+	MutexAutoLock lock(g_mutex);
+	auto i = g_values.find(name);
 	if(i == g_values.end()){
 		g_values[name] = val;
 		g_names.push_back(name);
diff --git a/src/util/quicktune.h b/src/util/quicktune.h
index 1943d19c2..14e7f5d7f 100644
--- a/src/util/quicktune.h
+++ b/src/util/quicktune.h
@@ -74,7 +74,7 @@ struct QuicktuneValue
 	void relativeAdd(float amount);
 };
 
-std::vector<std::string> getQuicktuneNames();
+const std::vector<std::string> &getQuicktuneNames();
 QuicktuneValue getQuicktuneValue(const std::string &name);
 void setQuicktuneValue(const std::string &name, const QuicktuneValue &val);