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: " <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 &objects, v3f po } } -void ServerEnvironment::clearAllObjects() +void ServerEnvironment::clearObjects(ClearObjectsMode mode) { - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Removing all active objects"< objects_to_remove; - for(std::map::iterator + for (std::map::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::iterator i = objects_to_remove.begin(); + for (std::vector::iterator i = objects_to_remove.begin(); i != objects_to_remove.end(); ++i) { m_active_objects.erase(*i); } // Get list of loaded blocks std::vector loaded_blocks; - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Listing all loaded blocks"<listAllLoadedBlocks(loaded_blocks); - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Done listing all loaded blocks: " - < loadable_blocks; - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Listing all loadable blocks"<listAllLoadableBlocks(loadable_blocks); - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Done listing all loadable blocks: " - <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::iterator i = loaded_blocks.begin(); + for (std::vector::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::iterator i = loadable_blocks.begin(); + for (std::vector::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 "<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 "<unloadUnreferencedBlocks(); } } m_map->unloadUnreferencedBlocks(); // Drop references that were added above - for(std::vector::iterator i = loaded_blocks.begin(); + for (std::vector::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 "< &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 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 {