Skip to content

Commit

Permalink
Add count based unload limit for mapblocks
Browse files Browse the repository at this point in the history
  • Loading branch information
est31 committed Aug 13, 2015
1 parent 2b04ab8 commit a8e238e
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 63 deletions.
3 changes: 3 additions & 0 deletions minetest.conf.example
Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions src/client.cpp
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/defaultsettings.cpp
Expand Up @@ -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");
Expand Down
138 changes: 107 additions & 31 deletions src/map.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/map.h
Expand Up @@ -277,7 +277,7 @@ class Map /*: public NodeContainer*/
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);

/*
Expand Down
35 changes: 20 additions & 15 deletions src/mapsector.cpp
Expand Up @@ -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())
Expand All @@ -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;
}

Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -125,7 +125,7 @@ void MapSector::deleteBlock(MapBlock *block)

// Clear from cache
m_block_cache = NULL;

// Remove from container
m_blocks.erase(block_y);

Expand All @@ -142,6 +142,11 @@ void MapSector::getBlocks(MapBlockVect &dest)
}
}

bool MapSector::empty()
{
return m_blocks.empty();
}

/*
ServerMapSector
*/
Expand All @@ -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
*/
Expand All @@ -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
*/
Expand Down

0 comments on commit a8e238e

Please sign in to comment.