Add count based unload limit for mapblocks
authorest31 <MTest31@outlook.com>
Mon, 10 Aug 2015 20:24:47 +0000 (22:24 +0200)
committerest31 <MTest31@outlook.com>
Thu, 13 Aug 2015 05:56:07 +0000 (07:56 +0200)
minetest.conf.example
src/client.cpp
src/defaultsettings.cpp
src/map.cpp
src/map.h
src/mapsector.cpp
src/mapsector.h
src/server.cpp

index f4b9057005e51f22e9df2cbc0b8841d0484754d8..de3cf3243b95539ff7df689778b79983a296bdb7 100644 (file)
@@ -94,6 +94,9 @@
 #random_input = false
 #    Timeout for client to remove unused map data from memory
 #client_unload_unused_data_timeout = 600
+#    Maximum number of mapblocks for client to be kept in memory
+#    Set to -1 for unlimited amount
+#client_mapblock_limit = 1000
 #    Whether to fog out the end of the visible area
 #enable_fog = true
 #    Whether to show the client debug info (has the same effect as hitting F5)
index d4d3b6df64b36d8dd49aa2fbcfa9db6cc2a2beef..946f4f1c48116447128534a9dfdd5b7b33c9604c 100644 (file)
@@ -421,8 +421,9 @@ void Client::step(float dtime)
                ScopeProfiler sp(g_profiler, "Client: map timer and unload");
                std::vector<v3s16> deleted_blocks;
                m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
-                               g_settings->getFloat("client_unload_unused_data_timeout"),
-                               &deleted_blocks);
+                       g_settings->getFloat("client_unload_unused_data_timeout"),
+                       g_settings->getS32("client_mapblock_limit"),
+                       &deleted_blocks);
 
                /*
                        Send info to server
index 40afc7dd353d33bae6ebaf01455f092ccd6ef6a2..92d85c8304d5aee6b5b4192348d53bc822adcfe0 100644 (file)
@@ -104,6 +104,7 @@ void set_default_settings(Settings *settings)
        settings->setDefault("address", "");
        settings->setDefault("random_input", "false");
        settings->setDefault("client_unload_unused_data_timeout", "600");
+       settings->setDefault("client_mapblock_limit", "1000");
        settings->setDefault("enable_fog", "true");
        settings->setDefault("fov", "72");
        settings->setDefault("view_bobbing", "true");
index 50b50220db23fdaaeaa42c9d91e54c86f72c4b57..38a700e3c9f6ee06c06cef454328da8bd01ced8f 100644 (file)
@@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "database-dummy.h"
 #include "database-sqlite3.h"
 #include <deque>
+#include <queue>
 #if USE_LEVELDB
 #include "database-leveldb.h"
 #endif
@@ -1399,10 +1400,25 @@ bool Map::getDayNightDiff(v3s16 blockpos)
        return false;
 }
 
+struct TimeOrderedMapBlock {
+       MapSector *sect;
+       MapBlock *block;
+
+       TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
+               sect(sect),
+               block(block)
+       {}
+
+       bool operator<(const TimeOrderedMapBlock &b) const
+       {
+               return block->getUsageTimer() < b.block->getUsageTimer();
+       };
+};
+
 /*
        Updates usage timers
 */
-void Map::timerUpdate(float dtime, float unload_timeout,
+void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
                std::vector<v3s16> *unloaded_blocks)
 {
        bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
@@ -1416,48 +1432,108 @@ void Map::timerUpdate(float dtime, float unload_timeout,
        u32 block_count_all = 0;
 
        beginSave();
-       for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
-               si != m_sectors.end(); ++si) {
-               MapSector *sector = si->second;
 
-               bool all_blocks_deleted = true;
+       // If there is no practical limit, we spare creation of mapblock_queue
+       if (max_loaded_blocks == (u32)-1) {
+               for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+                               si != m_sectors.end(); ++si) {
+                       MapSector *sector = si->second;
 
-               MapBlockVect blocks;
-               sector->getBlocks(blocks);
+                       bool all_blocks_deleted = true;
 
-               for(MapBlockVect::iterator i = blocks.begin();
-                               i != blocks.end(); ++i) {
-                       MapBlock *block = (*i);
+                       MapBlockVect blocks;
+                       sector->getBlocks(blocks);
 
-                       block->incrementUsageTimer(dtime);
+                       for (MapBlockVect::iterator i = blocks.begin();
+                                       i != blocks.end(); ++i) {
+                               MapBlock *block = (*i);
 
-                       if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
-                               v3s16 p = block->getPos();
+                               block->incrementUsageTimer(dtime);
 
-                               // Save if modified
-                               if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
-                                       modprofiler.add(block->getModifiedReasonString(), 1);
-                                       if (!saveBlock(block))
-                                               continue;
-                                       saved_blocks_count++;
-                               }
+                               if (block->refGet() == 0
+                                               && block->getUsageTimer() > unload_timeout) {
+                                       v3s16 p = block->getPos();
 
-                               // Delete from memory
-                               sector->deleteBlock(block);
+                                       // Save if modified
+                                       if (block->getModified() != MOD_STATE_CLEAN
+                                                       && save_before_unloading) {
+                                               modprofiler.add(block->getModifiedReasonString(), 1);
+                                               if (!saveBlock(block))
+                                                       continue;
+                                               saved_blocks_count++;
+                                       }
 
-                               if(unloaded_blocks)
-                                       unloaded_blocks->push_back(p);
+                                       // Delete from memory
+                                       sector->deleteBlock(block);
+
+                                       if (unloaded_blocks)
+                                               unloaded_blocks->push_back(p);
+
+                                       deleted_blocks_count++;
+                               } else {
+                                       all_blocks_deleted = false;
+                                       block_count_all++;
+                               }
+                       }
 
-                               deleted_blocks_count++;
+                       if (all_blocks_deleted) {
+                               sector_deletion_queue.push_back(si->first);
                        }
-                       else {
-                               all_blocks_deleted = false;
-                               block_count_all++;
+               }
+       } else {
+               std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
+               for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+                               si != m_sectors.end(); ++si) {
+                       MapSector *sector = si->second;
+
+                       MapBlockVect blocks;
+                       sector->getBlocks(blocks);
+
+                       for(MapBlockVect::iterator i = blocks.begin();
+                                       i != blocks.end(); ++i) {
+                               MapBlock *block = (*i);
+
+                               block->incrementUsageTimer(dtime);
+                               mapblock_queue.push(TimeOrderedMapBlock(sector, block));
                        }
                }
+               block_count_all = mapblock_queue.size();
+               // Delete old blocks, and blocks over the limit from the memory
+               while (mapblock_queue.size() > max_loaded_blocks
+                               || mapblock_queue.top().block->getUsageTimer() > unload_timeout) {
+                       TimeOrderedMapBlock b = mapblock_queue.top();
+                       mapblock_queue.pop();
+
+                       MapBlock *block = b.block;
+
+                       if (block->refGet() != 0)
+                               continue;
+
+                       v3s16 p = block->getPos();
+
+                       // Save if modified
+                       if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
+                               modprofiler.add(block->getModifiedReasonString(), 1);
+                               if (!saveBlock(block))
+                                       continue;
+                               saved_blocks_count++;
+                       }
 
-               if(all_blocks_deleted) {
-                       sector_deletion_queue.push_back(si->first);
+                       // Delete from memory
+                       b.sect->deleteBlock(block);
+
+                       if (unloaded_blocks)
+                               unloaded_blocks->push_back(p);
+
+                       deleted_blocks_count++;
+                       block_count_all--;
+               }
+               // Delete empty sectors
+               for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+                       si != m_sectors.end(); ++si) {
+                       if (si->second->empty()) {
+                               sector_deletion_queue.push_back(si->first);
+                       }
                }
        }
        endSave();
@@ -1484,7 +1560,7 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 
 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
 {
-       timerUpdate(0.0, -1.0, unloaded_blocks);
+       timerUpdate(0.0, -1.0, 0, unloaded_blocks);
 }
 
 void Map::deleteSectors(std::vector<v2s16> &sectorList)
index 5500ccf91bece71a3ac21274073c1ad2b8a47e53..2afd09639507feb7d04ac1f382a83cf0b0aef495 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -277,7 +277,7 @@ public:
                Updates usage timers and unloads unused blocks and sectors.
                Saves modified blocks before unloading on MAPTYPE_SERVER.
        */
-       void timerUpdate(float dtime, float unload_timeout,
+       void timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
                        std::vector<v3s16> *unloaded_blocks=NULL);
 
        /*
index 3fe81dc902e13c943b9af7e990ff5e082d4cd75f..9ce3c8eb321666204a3a288fe1e9701c2eccd132 100644 (file)
@@ -59,7 +59,7 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
        if(m_block_cache != NULL && y == m_block_cache_y){
                return m_block_cache;
        }
-       
+
        // If block doesn't exist, return NULL
        std::map<s16, MapBlock*>::iterator n = m_blocks.find(y);
        if(n == m_blocks.end())
@@ -70,11 +70,11 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
        else{
                block = n->second;
        }
-       
+
        // Cache the last result
        m_block_cache_y = y;
        m_block_cache = block;
-       
+
        return block;
 }
 
@@ -88,16 +88,16 @@ MapBlock * MapSector::createBlankBlockNoInsert(s16 y)
        assert(getBlockBuffered(y) == NULL);    // Pre-condition
 
        v3s16 blockpos_map(m_pos.X, y, m_pos.Y);
-       
+
        MapBlock *block = new MapBlock(m_parent, blockpos_map, m_gamedef);
-       
+
        return block;
 }
 
 MapBlock * MapSector::createBlankBlock(s16 y)
 {
        MapBlock *block = createBlankBlockNoInsert(y);
-       
+
        m_blocks[y] = block;
 
        return block;
@@ -114,7 +114,7 @@ void MapSector::insertBlock(MapBlock *block)
 
        v2s16 p2d(block->getPos().X, block->getPos().Z);
        assert(p2d == m_pos);
-       
+
        // Insert into container
        m_blocks[block_y] = block;
 }
@@ -125,7 +125,7 @@ void MapSector::deleteBlock(MapBlock *block)
 
        // Clear from cache
        m_block_cache = NULL;
-       
+
        // Remove from container
        m_blocks.erase(block_y);
 
@@ -142,6 +142,11 @@ void MapSector::getBlocks(MapBlockVect &dest)
        }
 }
 
+bool MapSector::empty()
+{
+       return m_blocks.empty();
+}
+
 /*
        ServerMapSector
 */
@@ -159,18 +164,18 @@ void ServerMapSector::serialize(std::ostream &os, u8 version)
 {
        if(!ser_ver_supported(version))
                throw VersionMismatchException("ERROR: MapSector format not supported");
-       
+
        /*
                [0] u8 serialization version
                + heightmap data
        */
-       
+
        // Server has both of these, no need to support not having them.
        //assert(m_objects != NULL);
 
        // Write version
        os.write((char*)&version, 1);
-       
+
        /*
                Add stuff here, if needed
        */
@@ -193,18 +198,18 @@ ServerMapSector* ServerMapSector::deSerialize(
        /*
                Read stuff
        */
-       
+
        // Read version
        u8 version = SER_FMT_VER_INVALID;
        is.read((char*)&version, 1);
-       
+
        if(!ser_ver_supported(version))
                throw VersionMismatchException("ERROR: MapSector format not supported");
-       
+
        /*
                Add necessary reading stuff here
        */
-       
+
        /*
                Get or create sector
        */
index e89247a92e0d1784d4817307385cc35910de5e5f..4c1ce86a3ac6077412f58ed7546e42a478539873 100644 (file)
@@ -40,7 +40,7 @@ class IGameDef;
 class MapSector
 {
 public:
-       
+
        MapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
        virtual ~MapSector();
 
@@ -58,16 +58,18 @@ public:
        MapBlock * createBlankBlock(s16 y);
 
        void insertBlock(MapBlock *block);
-       
+
        void deleteBlock(MapBlock *block);
-       
+
        void getBlocks(MapBlockVect &dest);
-       
+
+       bool empty();
+
        // Always false at the moment, because sector contains no metadata.
        bool differs_from_disk;
 
 protected:
-       
+
        // The pile of MapBlocks
        std::map<s16, MapBlock*> m_blocks;
 
@@ -76,12 +78,12 @@ protected:
        v2s16 m_pos;
 
        IGameDef *m_gamedef;
-       
+
        // Last-used block is cached here for quicker access.
-       // Be sure to set this to NULL when the cached block is deleted 
+       // Be sure to set this to NULL when the cached block is deleted
        MapBlock *m_block_cache;
        s16 m_block_cache_y;
-       
+
        /*
                Private methods
        */
@@ -94,7 +96,7 @@ class ServerMapSector : public MapSector
 public:
        ServerMapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
        ~ServerMapSector();
-       
+
        u32 getId() const
        {
                return MAPSECTOR_SERVER;
@@ -106,7 +108,7 @@ public:
        */
 
        void serialize(std::ostream &os, u8 version);
-       
+
        static ServerMapSector* deSerialize(
                        std::istream &is,
                        Map *parent,
@@ -114,7 +116,7 @@ public:
                        std::map<v2s16, MapSector*> & sectors,
                        IGameDef *gamedef
                );
-               
+
 private:
 };
 
@@ -124,7 +126,7 @@ class ClientMapSector : public MapSector
 public:
        ClientMapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
        ~ClientMapSector();
-       
+
        u32 getId() const
        {
                return MAPSECTOR_CLIENT;
@@ -133,6 +135,6 @@ public:
 private:
 };
 #endif
-       
+
 #endif
 
index 144107675c07941afa607390de40c31c1fc94142..dc7b101a619d6b51679562f03be947ac54158b27 100644 (file)
@@ -594,7 +594,8 @@ void Server::AsyncRunStep(bool initial_step)
                // Run Map's timers and unload unused data
                ScopeProfiler sp(g_profiler, "Server: map timer and unload");
                m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
-                               g_settings->getFloat("server_unload_unused_data_timeout"));
+                       g_settings->getFloat("server_unload_unused_data_timeout"),
+                       (u32)-1);
        }
 
        /*