From b1428ab4bb1e2cf73bc8ac951d41d22203ea68a0 Mon Sep 17 00:00:00 2001
From: Kahrl <kahrl@gmx.net>
Date: Mon, 8 Feb 2016 22:20:04 +0100
Subject: [PATCH] Add '/clearobjects quick'

---
 builtin/game/chatcommands.lua |  12 +++-
 doc/lua_api.txt               |   8 ++-
 src/environment.cpp           | 115 +++++++++++++++++++++-------------
 src/environment.h             |  20 +++++-
 src/script/lua_api/l_env.cpp  |  18 +++++-
 src/script/lua_api/l_env.h    |   2 +
 6 files changed, 124 insertions(+), 51 deletions(-)

diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
index fff893b28..9557a27c4 100644
--- a/builtin/game/chatcommands.lua
+++ b/builtin/game/chatcommands.lua
@@ -848,14 +848,24 @@ core.register_chatcommand("kick", {
 })
 
 core.register_chatcommand("clearobjects", {
+	params = "[full|quick]",
 	description = "clear all objects in world",
 	privs = {server=true},
 	func = function(name, param)
+		options = {}
+		if param == "" or param == "full" then
+			options.mode = "full"
+		elseif param == "quick" then
+			options.mode = "quick"
+		else
+			return false, "Invalid usage, see /help clearobjects."
+		end
+
 		core.log("action", name .. " clears all objects.")
 		core.chat_send_all("Clearing all objects.  This may take long."
 				.. "  You may experience a timeout.  (by "
 				.. name .. ")")
-		core.clear_objects()
+		core.clear_objects(options)
 		core.log("action", "Object clearing done.")
 		core.chat_send_all("*** Cleared all objects.")
 	end,
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index b6bc957c1..5f4e06423 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2046,8 +2046,12 @@ and `minetest.auth_reload` call the authetification handler.
 * `minetest.generate_decorations(vm, pos1, pos2)`
     * Generate all registered decorations within the VoxelManip `vm` and in the area from `pos1` to `pos2`.
     * `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
-* `minetest.clear_objects()`
-    * clear all objects in the environments
+* `minetest.clear_objects([options])`
+    * Clear all objects in the environment
+    * Takes an optional table as an argument with the field `mode`.
+        * mode = `"full"`: Load and go through every mapblock, clearing objects (default).
+        * mode = `"quick"`: Clear objects immediately in loaded mapblocks;
+          clear objects in unloaded mapblocks only when the mapblocks are next activated.
 * `minetest.emerge_area(pos1, pos2, [callback], [param])`
     * Queue all blocks in the area from `pos1` to `pos2`, inclusive, to be asynchronously
     * fetched from memory, loaded from disk, or if inexistent, generates them.
diff --git a/src/environment.cpp b/src/environment.cpp
index 38316cb31..3bf5e1f0a 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -354,6 +354,7 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
 	m_active_block_interval_overload_skip(0),
 	m_game_time(0),
 	m_game_time_fraction_counter(0),
+	m_last_clear_objects_time(0),
 	m_recommended_send_interval(0.1),
 	m_max_lag_estimate(0.1)
 {
@@ -503,6 +504,7 @@ void ServerEnvironment::saveMeta()
 	Settings args;
 	args.setU64("game_time", m_game_time);
 	args.setU64("time_of_day", getTimeOfDay());
+	args.setU64("last_clear_objects_time", m_last_clear_objects_time);
 	args.writeLines(ss);
 	ss<<"EnvArgsEnd\n";
 
@@ -546,6 +548,13 @@ void ServerEnvironment::loadMeta()
 		// This is not as important
 		setTimeOfDay(9000);
 	}
+
+	try {
+		m_last_clear_objects_time = args.getU64("last_clear_objects_time");
+	} catch (SettingNotFoundException &e) {
+		// If missing, do as if clearObjects was never called
+		m_last_clear_objects_time = 0;
+	}
 }
 
 struct ActiveABM
@@ -739,13 +748,19 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 	// Get time difference
 	u32 dtime_s = 0;
 	u32 stamp = block->getTimestamp();
-	if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
-		dtime_s = m_game_time - block->getTimestamp();
+	if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
+		dtime_s = m_game_time - stamp;
 	dtime_s += additional_dtime;
 
 	/*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
 			<<stamp<<", game time: "<<m_game_time<<std::endl;*/
 
+	// Remove stored static objects if clearObjects was called since block's timestamp
+	if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
+		block->m_static_objects.m_stored.clear();
+		// do not set changed flag to avoid unnecessary mapblock writes
+	}
+
 	// Set current time as timestamp
 	block->setTimestampNoChangedFlag(m_game_time);
 
@@ -858,22 +873,22 @@ void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f po
 	}
 }
 
-void ServerEnvironment::clearAllObjects()
+void ServerEnvironment::clearObjects(ClearObjectsMode mode)
 {
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Removing all active objects"<<std::endl;
+	infostream << "ServerEnvironment::clearObjects(): "
+		<< "Removing all active objects" << std::endl;
 	std::vector<u16> objects_to_remove;
-	for(std::map<u16, ServerActiveObject*>::iterator
+	for (std::map<u16, ServerActiveObject*>::iterator
 			i = m_active_objects.begin();
 			i != m_active_objects.end(); ++i) {
 		ServerActiveObject* obj = i->second;
-		if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+		if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
 			continue;
 		u16 id = i->first;
 		// Delete static object if block is loaded
-		if(obj->m_static_exists){
+		if (obj->m_static_exists) {
 			MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
-			if(block){
+			if (block) {
 				block->m_static_objects.remove(id);
 				block->raiseModified(MOD_STATE_WRITE_NEEDED,
 						MOD_REASON_CLEAR_ALL_OBJECTS);
@@ -881,7 +896,7 @@ void ServerEnvironment::clearAllObjects()
 			}
 		}
 		// If known by some client, don't delete immediately
-		if(obj->m_known_by_count > 0){
+		if (obj->m_known_by_count > 0) {
 			obj->m_pending_deactivation = true;
 			obj->m_removed = true;
 			continue;
@@ -893,39 +908,46 @@ void ServerEnvironment::clearAllObjects()
 		m_script->removeObjectReference(obj);
 
 		// Delete active object
-		if(obj->environmentDeletes())
+		if (obj->environmentDeletes())
 			delete obj;
 		// Id to be removed from m_active_objects
 		objects_to_remove.push_back(id);
 	}
 
 	// Remove references from m_active_objects
-	for(std::vector<u16>::iterator i = objects_to_remove.begin();
+	for (std::vector<u16>::iterator i = objects_to_remove.begin();
 			i != objects_to_remove.end(); ++i) {
 		m_active_objects.erase(*i);
 	}
 
 	// Get list of loaded blocks
 	std::vector<v3s16> loaded_blocks;
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Listing all loaded blocks"<<std::endl;
+	infostream << "ServerEnvironment::clearObjects(): "
+		<< "Listing all loaded blocks" << std::endl;
 	m_map->listAllLoadedBlocks(loaded_blocks);
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Done listing all loaded blocks: "
-			<<loaded_blocks.size()<<std::endl;
+	infostream << "ServerEnvironment::clearObjects(): "
+		<< "Done listing all loaded blocks: "
+		<< loaded_blocks.size()<<std::endl;
 
 	// Get list of loadable blocks
 	std::vector<v3s16> loadable_blocks;
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Listing all loadable blocks"<<std::endl;
-	m_map->listAllLoadableBlocks(loadable_blocks);
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Done listing all loadable blocks: "
-			<<loadable_blocks.size()
-			<<", now clearing"<<std::endl;
+	if (mode == CLEAR_OBJECTS_MODE_FULL) {
+		infostream << "ServerEnvironment::clearObjects(): "
+			<< "Listing all loadable blocks" << std::endl;
+		m_map->listAllLoadableBlocks(loadable_blocks);
+		infostream << "ServerEnvironment::clearObjects(): "
+			<< "Done listing all loadable blocks: "
+			<< loadable_blocks.size() << std::endl;
+	} else {
+		loadable_blocks = loaded_blocks;
+	}
+
+	infostream << "ServerEnvironment::clearObjects(): "
+		<< "Now clearing objects in " << loadable_blocks.size()
+		<< " blocks" << std::endl;
 
 	// Grab a reference on each loaded block to avoid unloading it
-	for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+	for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
 			i != loaded_blocks.end(); ++i) {
 		v3s16 p = *i;
 		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
@@ -934,24 +956,27 @@ void ServerEnvironment::clearAllObjects()
 	}
 
 	// Remove objects in all loadable blocks
-	u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
-	unload_interval = MYMAX(unload_interval, 1);
+	u32 unload_interval = U32_MAX;
+	if (mode == CLEAR_OBJECTS_MODE_FULL) {
+		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;
 	u32 num_objs_cleared = 0;
-	for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
+	for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
 			i != loadable_blocks.end(); ++i) {
 		v3s16 p = *i;
 		MapBlock *block = m_map->emergeBlock(p, false);
-		if(!block){
-			errorstream<<"ServerEnvironment::clearAllObjects(): "
-					<<"Failed to emerge block "<<PP(p)<<std::endl;
+		if (!block) {
+			errorstream << "ServerEnvironment::clearObjects(): "
+				<< "Failed to emerge block " << PP(p) << std::endl;
 			continue;
 		}
 		u32 num_stored = block->m_static_objects.m_stored.size();
 		u32 num_active = block->m_static_objects.m_active.size();
-		if(num_stored != 0 || num_active != 0){
+		if (num_stored != 0 || num_active != 0) {
 			block->m_static_objects.m_stored.clear();
 			block->m_static_objects.m_active.clear();
 			block->raiseModified(MOD_STATE_WRITE_NEEDED,
@@ -961,23 +986,23 @@ void ServerEnvironment::clearAllObjects()
 		}
 		num_blocks_checked++;
 
-		if(report_interval != 0 &&
-				num_blocks_checked % report_interval == 0){
+		if (report_interval != 0 &&
+				num_blocks_checked % report_interval == 0) {
 			float percent = 100.0 * (float)num_blocks_checked /
-					loadable_blocks.size();
-			infostream<<"ServerEnvironment::clearAllObjects(): "
-					<<"Cleared "<<num_objs_cleared<<" objects"
-					<<" in "<<num_blocks_cleared<<" blocks ("
-					<<percent<<"%)"<<std::endl;
+				loadable_blocks.size();
+			infostream << "ServerEnvironment::clearObjects(): "
+				<< "Cleared " << num_objs_cleared << " objects"
+				<< " in " << num_blocks_cleared << " blocks ("
+				<< percent << "%)" << std::endl;
 		}
-		if(num_blocks_checked % unload_interval == 0){
+		if (num_blocks_checked % unload_interval == 0) {
 			m_map->unloadUnreferencedBlocks();
 		}
 	}
 	m_map->unloadUnreferencedBlocks();
 
 	// Drop references that were added above
-	for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+	for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
 			i != loaded_blocks.end(); ++i) {
 		v3s16 p = *i;
 		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
@@ -985,9 +1010,11 @@ void ServerEnvironment::clearAllObjects()
 		block->refDrop();
 	}
 
-	infostream<<"ServerEnvironment::clearAllObjects(): "
-			<<"Finished: Cleared "<<num_objs_cleared<<" objects"
-			<<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
+	m_last_clear_objects_time = m_game_time;
+
+	infostream << "ServerEnvironment::clearObjects(): "
+		<< "Finished: Cleared " << num_objs_cleared << " objects"
+		<< " in " << num_blocks_cleared << " blocks" << std::endl;
 }
 
 void ServerEnvironment::step(float dtime)
diff --git a/src/environment.h b/src/environment.h
index 1aaa5091f..e7b818dc9 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -203,6 +203,18 @@ public:
 private:
 };
 
+/*
+	Operation mode for ServerEnvironment::clearObjects()
+*/
+enum ClearObjectsMode {
+	// Load and go through every mapblock, clearing objects
+	CLEAR_OBJECTS_MODE_FULL,
+
+	// Clear objects immediately in loaded mapblocks;
+	// clear objects in unloaded mapblocks only when the mapblocks are next activated.
+	CLEAR_OBJECTS_MODE_QUICK,
+};
+
 /*
 	The server-side environment.
 
@@ -319,8 +331,8 @@ public:
 	// Find all active objects inside a radius around a point
 	void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
 
-	// Clear all objects, loading and going through every MapBlock
-	void clearAllObjects();
+	// Clear objects, loading and going through every MapBlock
+	void clearObjects(ClearObjectsMode mode);
 
 	// This makes stuff happen
 	void step(f32 dtime);
@@ -410,6 +422,10 @@ private:
 	u32 m_game_time;
 	// A helper variable for incrementing the latter
 	float m_game_time_fraction_counter;
+	// Time of last clearObjects call (game time).
+	// When a mapblock older than this is loaded, its objects are cleared.
+	u32 m_last_clear_objects_time;
+	// Active block modifiers
 	std::vector<ABMWithState> m_abms;
 	// An interval for generally sending object positions and stuff
 	float m_recommended_send_interval;
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index 084b1b440..b445b1eb9 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -36,6 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "emerge.h"
 #include "pathfinder.h"
 
+struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
+{
+	{CLEAR_OBJECTS_MODE_FULL,  "full"},
+	{CLEAR_OBJECTS_MODE_QUICK, "quick"},
+	{0, NULL},
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
@@ -727,13 +734,20 @@ int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
 	return 1;
 }
 
-// clear_objects()
+// clear_objects([options])
 // clear all objects in the environment
+// where options = {mode = "full" or "quick"}
 int ModApiEnvMod::l_clear_objects(lua_State *L)
 {
 	GET_ENV_PTR;
 
-	env->clearAllObjects();
+	ClearObjectsMode mode = CLEAR_OBJECTS_MODE_FULL;
+	if (lua_istable(L, 1)) {
+		mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
+			ModApiEnvMod::es_ClearObjectsMode, mode);
+	}
+
+	env->clearObjects(mode);
 	return 0;
 }
 
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index 424556d4b..4f8dfcd3c 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -170,6 +170,8 @@ private:
 
 public:
 	static void Initialize(lua_State *L, int top);
+
+	static struct EnumString es_ClearObjectsMode[];
 };
 
 class LuaABM : public ActiveBlockModifier {