mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-10-30 23:15:32 +01:00 
			
		
		
		
	Add count based unload limit for mapblocks
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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"); | ||||
|   | ||||
							
								
								
									
										142
									
								
								src/map.cpp
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								src/map.cpp
									
									
									
									
									
								
							| @@ -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(); | ||||
| 
 | ||||
| 					// Save if modified
 | ||||
| 					if (block->getModified() != MOD_STATE_CLEAN | ||||
| 							&& save_before_unloading) { | ||||
| 						modprofiler.add(block->getModifiedReasonString(), 1); | ||||
| 						if (!saveBlock(block)) | ||||
| 							continue; | ||||
| 						saved_blocks_count++; | ||||
| 					} | ||||
| 
 | ||||
| 					// 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++; | ||||
| 				} | ||||
| 
 | ||||
| 				// 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++; | ||||
| 
 | ||||
| 			if (all_blocks_deleted) { | ||||
| 				sector_deletion_queue.push_back(si->first); | ||||
| 			} | ||||
| 		} | ||||
| 	} 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; | ||||
| 
 | ||||
| 		if(all_blocks_deleted) { | ||||
| 			sector_deletion_queue.push_back(si->first); | ||||
| 			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++; | ||||
| 			} | ||||
| 
 | ||||
| 			// 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> §orList) | ||||
|   | ||||
| @@ -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); | ||||
| 
 | ||||
| 	/*
 | ||||
|   | ||||
| @@ -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 | ||||
| 	*/ | ||||
|   | ||||
| @@ -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 | ||||
| 
 | ||||
|   | ||||
| @@ -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); | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user