From e988df0fbdd9d568889a28640c189ae022e99f8e Mon Sep 17 00:00:00 2001
From: Kahrl <kahrl@gmx.net>
Date: Sun, 2 Jun 2013 15:35:29 +0200
Subject: [PATCH] Add and implement setting
 max_clearobjects_extra_loaded_blocks.

Now Environment::clearAllObjects() unloads unused blocks in an interval
defined by max_clearobjects_extra_loaded_blocks (default 4096).
---
 minetest.conf.example   |  4 ++++
 src/defaultsettings.cpp |  1 +
 src/environment.cpp     | 39 +++++++++++++++++++++++++++++++++++++++
 src/map.cpp             | 25 +++++++++++++++++++++++++
 src/map.h               |  8 +++++++-
 5 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/minetest.conf.example b/minetest.conf.example
index af75438ff..4fd443db7 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -254,6 +254,10 @@
 #max_block_send_distance = 10
 # From how far blocks are generated for clients (value * 16 nodes)
 #max_block_generate_distance = 6
+# Number of extra blocks that can be loaded by /clearobjects at once
+# This is a trade-off between sqlite transaction overhead and
+# memory consumption (4096=100MB, as a rule of thumb)
+#max_clearobjects_extra_loaded_blocks = 4096
 # Interval of sending time of day to clients
 #time_send_interval = 5
 # Length of day/night cycle. 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index f270a47aa..d2bed7ed8 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -175,6 +175,7 @@ void set_default_settings(Settings *settings)
 	settings->setDefault("max_simultaneous_block_sends_server_total", "20");
 	settings->setDefault("max_block_send_distance", "9");
 	settings->setDefault("max_block_generate_distance", "7");
+	settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
 	settings->setDefault("time_send_interval", "5");
 	settings->setDefault("time_speed", "72");
 	settings->setDefault("server_unload_unused_data_timeout", "29");
diff --git a/src/environment.cpp b/src/environment.cpp
index 83ae59014..ab6a6d3d3 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -945,6 +945,16 @@ void ServerEnvironment::clearAllObjects()
 		m_active_objects.erase(*i);
 	}
 
+	// Get list of loaded blocks
+	std::list<v3s16> loaded_blocks;
+	infostream<<"ServerEnvironment::clearAllObjects(): "
+			<<"Listing all loaded blocks"<<std::endl;
+	m_map->listAllLoadedBlocks(loaded_blocks);
+	infostream<<"ServerEnvironment::clearAllObjects(): "
+			<<"Done listing all loaded blocks: "
+			<<loaded_blocks.size()<<std::endl;
+
+	// Get list of loadable blocks
 	std::list<v3s16> loadable_blocks;
 	infostream<<"ServerEnvironment::clearAllObjects(): "
 			<<"Listing all loadable blocks"<<std::endl;
@@ -953,6 +963,20 @@ void ServerEnvironment::clearAllObjects()
 			<<"Done listing all loadable blocks: "
 			<<loadable_blocks.size()
 			<<", now clearing"<<std::endl;
+
+	// Grab a reference on each loaded block to avoid unloading it
+	for(std::list<v3s16>::iterator i = loaded_blocks.begin();
+			i != loaded_blocks.end(); ++i)
+	{
+		v3s16 p = *i;
+		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+		assert(block);
+		block->refGrab();
+	}
+
+	// Remove objects in all loadable blocks
+	u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
+	unload_interval = MYMAX(unload_interval, 1);
 	u32 report_interval = loadable_blocks.size() / 10;
 	u32 num_blocks_checked = 0;
 	u32 num_blocks_cleared = 0;
@@ -987,7 +1011,22 @@ void ServerEnvironment::clearAllObjects()
 					<<" in "<<num_blocks_cleared<<" blocks ("
 					<<percent<<"%)"<<std::endl;
 		}
+		if(num_blocks_checked % unload_interval == 0){
+			m_map->unloadUnreferencedBlocks();
+		}
 	}
+	m_map->unloadUnreferencedBlocks();
+
+	// Drop references that were added above
+	for(std::list<v3s16>::iterator i = loaded_blocks.begin();
+			i != loaded_blocks.end(); ++i)
+	{
+		v3s16 p = *i;
+		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+		assert(block);
+		block->refDrop();
+	}
+
 	infostream<<"ServerEnvironment::clearAllObjects(): "
 			<<"Finished: Cleared "<<num_objs_cleared<<" objects"
 			<<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
diff --git a/src/map.cpp b/src/map.cpp
index 43502253b..7439076d3 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1510,6 +1510,11 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 	}
 }
 
+void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
+{
+	timerUpdate(0.0, -1.0, unloaded_blocks);
+}
+
 void Map::deleteSectors(std::list<v2s16> &list)
 {
 	for(std::list<v2s16>::iterator j = list.begin();
@@ -3409,6 +3414,26 @@ void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
 	}
 }
 
+void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
+{
+	for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+		si != m_sectors.end(); ++si)
+	{
+		MapSector *sector = si->second;
+
+		std::list<MapBlock*> blocks;
+		sector->getBlocks(blocks);
+
+		for(std::list<MapBlock*>::iterator i = blocks.begin();
+				i != blocks.end(); ++i)
+		{
+			MapBlock *block = (*i);
+			v3s16 p = block->getPos();
+			dst.push_back(p);
+		}
+	}
+}
+
 void ServerMap::saveMapMeta()
 {
 	DSTACK(__FUNCTION_NAME);
diff --git a/src/map.h b/src/map.h
index 31001e4c3..530d81e7a 100644
--- a/src/map.h
+++ b/src/map.h
@@ -279,6 +279,12 @@ class Map /*: public NodeContainer*/
 	void timerUpdate(float dtime, float unload_timeout,
 			std::list<v3s16> *unloaded_blocks=NULL);
 
+	/*
+		Unloads all blocks with a zero refCount().
+		Saves modified blocks before unloading on MAPTYPE_SERVER.
+	*/
+	void unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks=NULL);
+
 	// Deletes sectors and their blocks from memory
 	// Takes cache into account
 	// If deleted sector is in sector cache, clears cache
@@ -433,8 +439,8 @@ class ServerMap : public Map
 	void endSave();
 
 	void save(ModifiedState save_level);
-	//void loadAll();
 	void listAllLoadableBlocks(std::list<v3s16> &dst);
+	void listAllLoadedBlocks(std::list<v3s16> &dst);
 	// Saves map seed and possibly other stuff
 	void saveMapMeta();
 	void loadMapMeta();
-- 
GitLab