Minetest源码分析十二:ServerMap

Minetest源码分析十二:ServerMap


minetest->map.h/map.cpp

ServerMap:生成地图的类,地图数据库的存储与访问,地图meta数据的存储与读取。

处理MapBlock的存储以及获取生成;在这个类中会与数据库或者其它类型数据文件进行通信处理;另serverMap是继承于Map类的,所以还包括了Map类中的一些操作,譬如MapSector的创建以及MapNode的获取、存储、remove等操作。


ServerMap创建与调用

在minetest/server.cpp中Server类的构造函数中创建,并存储在ServerEnvironment对象中,当程序运行时,只有一个ServerEnvironment环境存在。所有后期都是通过ServerEnvironment获取出来的。

ServerMap *servermap = new ServerMap(path_world, this, m_emerge);

m_env = new ServerEnvironment(servermap, m_script, this);


主要使用的地方:ServerEnvironment类以及EmergeThread类中。ServerEnvironment类是存储它的地方,EmergeThread类中使用它来创建生成MapBlock


ServerMap定义

继承Map




重要函数介绍

(1)ServerMap构造函数

加载地图数据库,若没有创建(根据world.mt);加载地图元数据(map_meta.txt: chunksize、mg_flags等)

ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge): Map(dout_server, gamedef), m_emerge(emerge), m_map_metadata_changed(true)
{
	/*
		Try to load map; if not found, create a new one.
	*/

	// Determine which database backend to use
	std::string conf_path = savedir + DIR_DELIM + "world.mt";
	Settings conf;
	bool succeeded = conf.readConfigFile(conf_path.c_str());
	if (!succeeded || !conf.exists("backend")) {
		// fall back to sqlite3
		dbase = new Database_SQLite3(this, savedir);
		conf.set("backend", "sqlite3");
	} else {
		std::string backend = conf.get("backend");
		if (backend == "dummy")
			dbase = new Database_Dummy(this);
		else if (backend == "sqlite3")
			dbase = new Database_SQLite3(this, savedir);
		#if USE_LEVELDB
		else if (backend == "leveldb")
			dbase = new Database_LevelDB(this, savedir);
		#endif
		#if USE_REDIS
		else if (backend == "redis")
			dbase = new Database_Redis(this, savedir);
		#endif
		else
			throw BaseException("Unknown map backend");
	}

	m_savedir = savedir;
	m_map_saving_enabled = false;

	try
	{
		// If directory exists, check contents and load if possible
		if(fs::PathExists(m_savedir))
		{
			// If directory is empty, it is safe to save into it.
			if(fs::GetDirListing(m_savedir).size() == 0)
			{
				infostream<<"ServerMap: Empty save directory is valid."
						<
   
   


(2)bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)


初始创建BlockMakeData,输入是block位置信息,返回BlockMakeData数据。之后一般需要使用mapgen->makeChunk(&data)来生成blockfinishBlockMake将数据设置到地图上,即设置到对应的block上。


调用:EmergeThread::getBlockOrStartGen()、MapBlock * ServerMap::generateBlock(),在这两个方法中调用。


initBlockMake这个函数作用:初始创建block在blockpos处的相关数据结构以及周围的一些数据结构,如block属于的MapSector、MapBlock、BlockMakeData。MapSector:内存已经有了不需要创建,若没有就创建空白的一个MapSector对象,最后存储在std::map<v2s16, MapSector*> m_sectors变量中,这个std::map变量存在Map对象中。MapBlock:内存中已经有了不需要创建,若没有就创建一个MapBlock对象,最后存储在MapBlock的x和z对应的MapSector中。MapSector 中有如下变量std::map<s16, MapBlock*> m_blocks; BlockMakeData:Block make 需要的一些数据,并不是block用于显示的具体数据,而是block make过程中需要用的普通参数。


bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
{
	bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;

	s16 chunksize = m_emerge->params.chunksize;
	s16 coffset = -chunksize / 2;
	v3s16 chunk_offset(coffset, coffset, coffset);
	v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
	v3s16 blockpos_min = blockpos_div * chunksize;
	v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
	blockpos_min += chunk_offset;
	blockpos_max += chunk_offset;

	v3s16 extra_borders(1,1,1);

	// Do nothing if not inside limits (+-1 because of neighbors)
	if(blockpos_over_limit(blockpos_min - extra_borders) ||
		blockpos_over_limit(blockpos_max + extra_borders))
		return false;

	data->seed = m_emerge->params.seed;
	data->blockpos_min = blockpos_min;
	data->blockpos_max = blockpos_max;
	data->blockpos_requested = blockpos;
	data->nodedef = m_gamedef->ndef();

	/*
		Create the whole area of this and the neighboring blocks
	*/
	{
		for(s16 x=blockpos_min.X-extra_borders.X; x<=blockpos_max.X+extra_borders.X; x++)
		for(s16 z=blockpos_min.Z-extra_borders.Z; z<=blockpos_max.Z+extra_borders.Z; z++)
		{
			v2s16 sectorpos(x, z);
			// Sector metadata is loaded from disk if not already loaded.
			ServerMapSector *sector = createSector(sectorpos);
			assert(sector);
			(void) sector;

			for(s16 y=blockpos_min.Y-extra_borders.Y;
					y<=blockpos_max.Y+extra_borders.Y; y++)
			{
				v3s16 p(x,y,z);
				//MapBlock *block = createBlock(p);
				// 1) get from memory, 2) load from disk
				MapBlock *block = emergeBlock(p, false);
				// 3) create a blank one
				if(block == NULL)
				{
					block = createBlock(p);

					/*
						Block gets sunlight if this is true.

						Refer to the map generator heuristics.
					*/
					bool ug = m_emerge->isBlockUnderground(p);
					block->setIsUnderground(ug);
				}

				// Lighting will not be valid after make_chunk is called
				block->setLightingExpired(true);
			}
		}
	}

	/*
		Now we have a big empty area.

		Make a ManualMapVoxelManipulator that contains this and the
		neighboring blocks
	*/

	// The area that contains this block and it's neighbors
	v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
	v3s16 bigarea_blocks_max = blockpos_max + extra_borders;

	data->vmanip = new ManualMapVoxelManipulator(this);

	// Add the area
	data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);

	// Data is ready now.
	return true;
}


涉及到的函数:

minetest->emerge.h

struct BlockMakeData {

MMVManip *vmanip;

u64 seed;

v3s16 blockpos_min;

v3s16 blockpos_max;

v3s16 blockpos_requested;

UniqueQueue<v3s16> transforming_liquid;

INodeDefManager *nodedef;


BlockMakeData():

vmanip(NULL),

seed(0),

nodedef(NULL)

{}


~BlockMakeData() { delete vmanip; }

};



minetest->mapsector.h

MapSector:管理 x z固定的blocks。如deleteBlock、insertBlock等。

主要成员变量:

std::map<s16, MapBlock*> m_blocks,管理的xz固定下的所有blocks


主要方法:

MapBlock * getBlockNoCreateNoEx(s16 y) 从内存中获取y位置的block。

MapBlock * createBlankBlock(s16 y) 创建位置y处的block。



minetest->mapblock.h

MapBlock:管理block的一些属性以及Node的一些属性以及获取。




(3)MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, std::map<v3s16, MapBlock*> &changed_blocks)

初始创建BlockMakeData,输入是block位置信息,返回BlockMakeData数据。之后一般需要使用mapgen->makeChunk(&data)来生成blockfinishBlockMake将数据设置到地图上,即设置到对应的block上。


功能:block的MapNode数据设置进去,并更新changed blocks的light、modified属性、Generated属性等。block的MapNode数据设置通过blitBackAll函数。


MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
		std::map
    
    
     
      &changed_blocks)
{
	v3s16 blockpos_min = data->blockpos_min;
	v3s16 blockpos_max = data->blockpos_max;
	v3s16 blockpos_requested = data->blockpos_requested;

	v3s16 extra_borders(1,1,1);

	bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;

	// Make sure affected blocks are loaded
	for(s16 x=blockpos_min.X-extra_borders.X;
			x<=blockpos_max.X+extra_borders.X; x++)
	for(s16 z=blockpos_min.Z-extra_borders.Z;
			z<=blockpos_max.Z+extra_borders.Z; z++)
	for(s16 y=blockpos_min.Y-extra_borders.Y;
			y<=blockpos_max.Y+extra_borders.Y; y++)
	{
		v3s16 p(x, y, z);
		// Load from disk if not already in memory
		emergeBlock(p, false);
	}

	/*
		Blit generated stuff to map
		NOTE: blitBackAll adds nearly everything to changed_blocks
	*/
	{
		data->vmanip->blitBackAll(&changed_blocks);
	}


	/*
		Copy transforming liquid information
	*/
	while(data->transforming_liquid.size() > 0)
	{
		v3s16 p = data->transforming_liquid.pop_front();
		m_transforming_liquid.push_back(p);
	}

	/*
		Do stuff in central blocks
	*/

	/*
		Update lighting
	*/
	{
		/*
			Set lighting to non-expired state in all of them.
			This is cheating, but it is not fast enough if all of them
			would actually be updated.
		*/
		for(s16 x=blockpos_min.X-extra_borders.X;
				x<=blockpos_max.X+extra_borders.X; x++)
		for(s16 z=blockpos_min.Z-extra_borders.Z;
				z<=blockpos_max.Z+extra_borders.Z; z++)
		for(s16 y=blockpos_min.Y-extra_borders.Y;
				y<=blockpos_max.Y+extra_borders.Y; y++)
		{
			v3s16 p(x, y, z);
			getBlockNoCreateNoEx(p)->setLightingExpired(false);
		}
	}

	/*
		Go through changed blocks
	*/
	for(std::map
     
     
      
      ::iterator i = changed_blocks.begin(); i != changed_blocks.end(); ++i)
	{
		MapBlock *block = i->second;
		assert(block);
		/*
			Update day/night difference cache of the MapBlocks
		*/
		block->expireDayNightDiff();
		/*
			Set block as modified
		*/
		block->raiseModified(MOD_STATE_WRITE_NEEDED,
				"finishBlockMake expireDayNightDiff");
	}

	/*
		Set central blocks as generated
	*/
	for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
	for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
	for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
	{
		v3s16 p(x, y, z);
		MapBlock *block = getBlockNoCreateNoEx(p);
		assert(block);
		block->setGenerated(true);
	}

	MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);

	return block;
}

     
     
    
    


主要涉及函数:

minetest->map.cpp

void ManualMapVoxelManipulator::blitBackAll(std::map<v3s16, MapBlock*> * modified_blocks)

拷贝好block的数据:block->copyFrom(*this)dst.copyTo(data, data_area, v3s16(0,0,0),getPosRelative(), data_size)dataMapBlockMapNode *data,即MapBlock的数据;返回modified_blocks



(4)void ServerMap::save(ModifiedState save_level)

功能:保存map的meta数据(map_meta.txt)、保存所有Sector的Meta数据、保存所有block数据(如sqlite文件map.sqlite)。

参数:ModifiedState save_level  程序运行中常用的是MOD_STATE_WRITE_NEEDED,即需要尽快保存。

enum ModifiedState

{

// Has not been modified.

MOD_STATE_CLEAN = 0,

MOD_RESERVED1 = 1,

// Has been modified, and will be saved when being unloaded.

MOD_STATE_WRITE_AT_UNLOAD = 2,

MOD_RESERVED3 = 3,

// Has been modified, and will be saved as soon as possible.

MOD_STATE_WRITE_NEEDED = 4,

MOD_RESERVED5 = 5,

};


调用:void Server::AsyncRunStep(bool initial_step) 中调用ServeMapsave函数m_env->getMap().save(MOD_STATE_WRITE_NEEDED); AsyncRunStep()方法主要在ServerThread类的线程函数中循环体中调用,具体如下:


minetest->server.cpp

void * ServerThread::Thread()

{


ThreadStarted();


while(!StopRequested())

{

try{

m_server->AsyncRunStep();


m_server->Receive();


}

。。。

}

}


void ServerMap::save(ModifiedState save_level)
{
	if(m_map_saving_enabled == false)
	{
		infostream<<"WARNING: Not saving map, saving disabled."<
    
    
     
     ::iterator i = m_sectors.begin();
		i != m_sectors.end(); ++i)
	{
		ServerMapSector *sector = (ServerMapSector*)i->second;
		assert(sector->getId() == MAPSECTOR_SERVER);

		if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
		{
			saveSectorMeta(sector);
			sector_meta_count++;
		}
		std::list
     
     
      
       blocks;
		sector->getBlocks(blocks);

		for(std::list
      
      
       
       ::iterator j = blocks.begin();
			j != blocks.end(); ++j)
		{
			MapBlock *block = *j;

			block_count_all++;

			if(block->getModified() >= (u32)save_level)
			{
				// Lazy beginSave()
				if(!save_started){
					beginSave();
					save_started = true;
				}

				modprofiler.add(block->getModifiedReason(), 1);

				saveBlock(block);
				block_count++;
			}
		}
	}
	if(save_started)
		endSave();
}

      
      
     
     
    
    



(5)void ServerMap::saveMapMeta()

minetest->emerge.cpp

m_emerge->saveParamsToSettings(&params);


void ServerMap::loadMapMeta()

minetest->emerge.cpp

m_emerge->loadParamsFromSettings(&params);



void ServerMap::saveBlock(MapBlock *block)

将block数据保存到数据库文件中。


void ServerMap::loadBlock

从数据文件中加载。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值