diff --git a/src/filesys.cpp b/src/filesys.cpp
index a2d3f9d145c2388da2db27ca7aa2c298f704c15f..a025ec18562872392cedcdf3968f542d3754236c 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "filesys.h"
 #include <iostream>
+#include <string.h>
 
 namespace fs
 {
@@ -130,12 +131,35 @@ bool PathExists(std::string path)
 	return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
 }
 
+bool RecursiveDelete(std::string path)
+{
+	std::cerr<<"Removing \""<<path<<"\""<<std::endl;
+
+	return false;
+	
+	// This silly function needs a double-null terminated string...
+	// Well, we'll just make sure it has at least two, then.
+	path += "\0\0";
+
+	SHFILEOPSTRUCT sfo;
+	sfo.hwnd = NULL;
+	sfo.wFunc = FO_DELETE;
+	sfo.pFrom = path.c_str();
+	sfo.pTo = NULL;
+	sfo.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
+	
+	int r = SHFileOperation(&sfo);
+
+	return (r == 0);
+}
+
 #else // POSIX
 
 #include <sys/types.h>
 #include <dirent.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 
 std::vector<DirListNode> GetDirListing(std::string pathstring)
 {
@@ -184,7 +208,70 @@ bool PathExists(std::string path)
 	return (stat(path.c_str(),&st) == 0);
 }
 
+bool RecursiveDelete(std::string path)
+{
+	/*
+		Execute the 'rm' command directly, by fork() and execve()
+	*/
+	
+	std::cerr<<"Removing \""<<path<<"\""<<std::endl;
+
+	//return false;
+	
+	pid_t child_pid = fork();
+
+	if(child_pid == 0)
+	{
+		// Child
+		char argv_data[3][10000];
+		strcpy(argv_data[0], "/bin/rm");
+		strcpy(argv_data[1], "-rf");
+		strncpy(argv_data[2], path.c_str(), 10000);
+		char *argv[4];
+		argv[0] = argv_data[0];
+		argv[1] = argv_data[1];
+		argv[2] = argv_data[2];
+		argv[3] = NULL;
+
+		std::cerr<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
+				<<argv[2]<<"'"<<std::endl;
+		
+		execv(argv[0], argv);
+		
+		// Execv shouldn't return. Failed.
+		return false;
+	}
+	else
+	{
+		// Parent
+		int child_status;
+		pid_t tpid;
+		do{
+			tpid = wait(&child_status);
+			//if(tpid != child_pid) process_terminated(tpid);
+		}while(tpid != child_pid);
+		return (child_status == 0);
+	}
+}
+
 #endif
 
+bool RecursiveDeleteContent(std::string path)
+{
+	std::cerr<<"Removing content of \""<<path<<"\""<<std::endl;
+	std::vector<DirListNode> list = GetDirListing(path);
+	for(unsigned int i=0; i<list.size(); i++)
+	{
+		std::string childpath = path+"/"+list[i].name;
+		bool r = RecursiveDelete(childpath);
+		if(r == false)
+		{
+			std::cerr<<"Removing \""<<childpath<<"\" failed"<<std::endl;
+			return false;
+		}
+	}
+	return true;
+}
+
 } // namespace fs
 
diff --git a/src/filesys.h b/src/filesys.h
index 6dbc426a5494454a075047300808e77071eb86b7..e46b17b0def460faace60aa16759a1004890be54 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -40,6 +40,12 @@ bool CreateDir(std::string path);
 
 bool PathExists(std::string path);
 
+// Only pass full paths to this one. True on success.
+bool RecursiveDelete(std::string path);
+
+// Only pass full paths to this one. True on success.
+bool RecursiveDeleteContent(std::string path);
+
 }//fs
 
 #endif
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index 84435b5f34fb797f8fcc99b3512dd9972cdc849a..2d02c0295f1238a9c56c95aa1956840fde0495e5 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -177,6 +177,12 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 		rect = rect + v2s32(size.X/2-180/2, size.Y/2-30/2 + 100);
 		Environment->addButton(rect, this, 257, L"Start Game / Connect");
 	}
+	// Map delete button
+	{
+		core::rect<s32> rect(0, 0, 130, 30);
+		rect = rect + v2s32(size.X/2-130/2+200, size.Y/2-30/2 + 100);
+		Environment->addButton(rect, this, 260, L"Delete map");
+	}
 }
 
 void GUIMainMenu::drawMenu()
@@ -252,10 +258,16 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
 		{
 			switch(event.GUIEvent.Caller->getID())
 			{
-			case 257:
+			case 257: // Start game
 				acceptInput();
 				quitMenu();
 				break;
+			case 260: // Delete map
+				// Don't accept input data, just set deletion request
+				m_data->delete_map = true;
+				m_accepted = true;
+				quitMenu();
+				break;
 			}
 		}
 		if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index 8060f511d75a6ebf61f71a1e3cf306a0ab92f51d..010d0bf6d975e71dc27b2c29d1bd7e69a39226c9 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -29,11 +29,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 struct MainMenuData
 {
+	MainMenuData():
+		creative_mode(false),
+		delete_map(false)
+	{}
 	// These are in the native format of the gui elements
 	std::wstring address;
 	std::wstring port;
 	std::wstring name;
 	bool creative_mode;
+	// If map deletion is requested, this is set to true
+	bool delete_map;
 };
 
 class GUIMainMenu : public GUIModalMenu
diff --git a/src/main.cpp b/src/main.cpp
index 171f1538a6f3e0e221b8c1c9d12607304dc2e3a1..da1b88d4a9db3220f865a57beb409000251f22f8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -153,8 +153,10 @@ TODO: Optimize day/night mesh updating somehow
 		   meshbuffers? It should go quite fast.
 		   - This is not easy; There'd need to be a buffer somewhere
 		     that would contain the night and day lighting values.
+			 - Actually if FastFaces would be stored, they could
+			   hold both values
 
-TODO: Combine MapBlock's face caches to so big pieces that VBO
+FEATURE: Combine MapBlock's face caches to so big pieces that VBO
       gets used
       - That is >500 vertices
 	  - This is not easy; all the MapBlocks close to the player would
@@ -181,6 +183,10 @@ TODO: Untie client network operations from framerate
 
 TODO: Make morning and evening shorter
 
+TODO: Don't update all meshes always on single node changes, but
+      check which ones should be updated
+	  - implement Map::updateNodeMeshes()
+
 Server:
 -------
 
@@ -260,10 +266,15 @@ FEATURE: The map could be generated procedually:
 		- How about relocating minerals, too? Coal and gold in
 		  downstream sand and gravel would be kind of cool
 		  - This would need a better way of handling minerals, mainly
-		    to have mineral content as a separate field
+		    to have mineral content as a separate field. the first
+			parameter field is free for this.
 		- Simulate rock falling from cliffs when water has removed
 		  enough solid rock from the bottom
 
+TODO: Mineral and ground material properties
+      - This way mineral ground toughness can be calculated with just
+	    some formula, as well as tool strengths
+
 TODO: Change AttributeList to split the area into smaller sections so
       that searching won't be as heavy.
 
@@ -308,6 +319,7 @@ Doing now:
 #pragma comment(lib, "Irrlicht.lib")
 //#pragma comment(lib, "jthread.lib")
 #pragma comment(lib, "zlibwapi.lib")
+#pragma comment(lib, "Shell32.lib")
 // This would get rid of the console window
 //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
 #endif
@@ -1270,9 +1282,12 @@ int main(int argc, char *argv[])
 	porting::initializePaths();
 	// Create user data directory
 	fs::CreateDir(porting::path_userdata);
-
+	
+	// C-style stuff initialization
 	initializeMaterialProperties();
+	init_mapnode();
 
+	// Debug handler
 	BEGIN_DEBUG_EXCEPTION_HANDLER
 
 	// Print startup message
@@ -1550,7 +1565,7 @@ int main(int argc, char *argv[])
 	*/
 
 	init_content_inventory_texture_paths();
-	init_tile_textures();
+	//init_tile_textures();
 
 	/*
 		GUI stuff
@@ -1608,10 +1623,10 @@ int main(int argc, char *argv[])
 	{
 	
 	/*
-		Out-of-game menu loop
+		Out-of-game menu loop.
+
+		Loop quits when menu returns proper parameters.
 	*/
-	
-	// Wait for proper parameters
 	for(;;)
 	{
 		// Cursor can be non-visible when coming from the game
@@ -1671,6 +1686,15 @@ int main(int argc, char *argv[])
 		dstream<<"Dropping main menu"<<std::endl;
 
 		menu->drop();
+		
+		// Delete map if requested
+		if(menudata.delete_map)
+		{
+			bool r = fs::RecursiveDeleteContent(map_dir);
+			if(r == false)
+				error_message = L"Delete failed";
+			continue;
+		}
 
 		playername = wide_to_narrow(menudata.name);
 		address = wide_to_narrow(menudata.address);
@@ -2386,8 +2410,20 @@ int main(int argc, char *argv[])
 
 			static float dig_time = 0.0;
 			static u16 dig_index = 0;
+			
+			// Visualize selection
+
+			const float d = 0.502;
+			core::aabbox3d<f32> nodebox(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
+			v3f nodepos_f = intToFloat(nodepos);
+			//v3f nodepos_f(nodepos.X*BS, nodepos.Y*BS, nodepos.Z*BS);
+			nodebox.MinEdge += nodepos_f;
+			nodebox.MaxEdge += nodepos_f;
+			hilightboxes.push_back(nodebox);
+			
+			//hilightboxes.push_back(nodefacebox);
 
-			hilightboxes.push_back(nodefacebox);
+			// Handle digging
 			
 			if(g_input->getLeftReleased())
 			{
@@ -2473,6 +2509,7 @@ int main(int argc, char *argv[])
 
 					if(dig_index < CRACK_ANIMATION_LENGTH)
 					{
+						//TimeTaker timer("client.setTempMod");
 						//dstream<<"dig_index="<<dig_index<<std::endl;
 						client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
 					}