Minetest源码分析十一:EmergeManager

Minetest源码分析十一:EmergeManager



EmergeThread:生成地图的线程类

class EmergeThread : public JThread
{
public:
	Server *m_server;
	ServerMap *map;
	EmergeManager *emerge;
	Mapgen *mapgen;
	bool enable_mapgen_debug_info;
	int id;

	Event qevent;
	std::queue
   
   
    
     blockqueue;

	EmergeThread(Server *server, int ethreadid): JThread(), m_server(server), map(NULL), emerge(NULL), mapgen(NULL), enable_mapgen_debug_info(false), id(ethreadid)
	{
	}

	void *Thread();
	bool popBlockEmerge(v3s16 *pos, u8 *flags);
	bool getBlockOrStartGen(v3s16 p, MapBlock **b,
			BlockMakeData *data, bool allow_generate);
};

   
   


成员变量:

Server *m_server:

EmergeManager *emerge:管理Emerge类,有可能支持多线程Emerge

ServerMap *map:服务端存储地图数据,以及地图初始化

Mapgen *mapgen:地图生成node的实例对象

std::queue<v3s16> blockqueue:存储blocks位置的队列。


std::queue<v3s16> blockqueue 什么时候变更值?

1)线程中变更、使用数据:EmergeThread::popBlockEmerge()方法中使用, v3s16 p = blockqueue.front();blockqueue.pop(); popBlockEmerge()只有在Thread()线程循环中一直使用,即一直循环每次获取一条blockqueuev3s16数据(需要生成的block位置信息)。


2)线程外变更数据:EmergeManager::enqueueBlockEmerge()这个函数中更改的,emergethread[idx]->blockqueue.push(p),在这里加入需要生成的block位置信息到blockqueue队列中




方法:

void *Thread();

bool popBlockEmerge(v3s16 *pos, u8 *flags);

bool getBlockOrStartGen(v3s16 p, MapBlock **b,BlockMakeData *data, bool allow_generate);

主要是Thread()这个线程函数,另外两个方法都是在线程中使用的。




1.void *Thread()

生成地图的线程,是一个循环,不断地使用popBlockEmerge()方法获取每次需要生成的block的位置,然后采用getBlockOrStartGen()方法,根据位置获取或者生成出来对应的MapBlock以及其BlockMeshData,最后更新设置server中的clients的not sent blocks列表。


流程图如下:




涉及函数如下:

void *EmergeThread::Thread() 
{
	ThreadStarted();
	log_register_thread("EmergeThread" + itos(id));
	DSTACK(__FUNCTION_NAME);
	BEGIN_DEBUG_EXCEPTION_HANDLER

	v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
	v3s16 p;
	u8 flags = 0;

	map    = (ServerMap *)&(m_server->m_env->getMap());
	emerge = m_server->m_emerge;
	mapgen = emerge->mapgen[id];
	enable_mapgen_debug_info = emerge->mapgen_debug_info;

	porting::setThreadName("EmergeThread");

	while (!StopRequested())
	try {
		if (!popBlockEmerge(&p, &flags)) {
			qevent.wait();
			continue;
		}

		last_tried_pos = p;
		if (blockpos_over_limit(p))
			continue;

		bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN;
		EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate);

		/*
			Try to fetch block from memory or disk.
			If not found and asked to generate, initialize generator.
		*/
		BlockMakeData data;
		MapBlock *block = NULL;
		std::map
   
   
    
     modified_blocks;

		if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) {
			{
				ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG);
				TimeTaker t("mapgen::make_block()");

				mapgen->makeChunk(&data);

				if (enable_mapgen_debug_info == false)
					t.stop(true); // Hide output
			}

			{
				//envlock: usually 0ms, but can take either 30 or 400ms to acquire
				JMutexAutoLock envlock(m_server->m_env_mutex);
				ScopeProfiler sp(g_profiler, "EmergeThread: after "
						"Mapgen::makeChunk (envlock)", SPT_AVG);

				map->finishBlockMake(&data, modified_blocks);

				block = map->getBlockNoCreateNoEx(p);
				if (block) {
					/*
						Do some post-generate stuff
					*/
					v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE;
					v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE +
								 v3s16(1,1,1) * (MAP_BLOCKSIZE - 1);

					// Ignore map edit events, they will not need to be sent
					// to anybody because the block hasn't been sent to anybody
					MapEditEventAreaIgnorer
						ign(&m_server->m_ignore_map_edit_events_area,
						VoxelArea(minp, maxp));
					try {  // takes about 90ms with -O1 on an e3-1230v2
						m_server->getScriptIface()->environment_OnGenerated(
								minp, maxp, emerge->getBlockSeed(minp));
					} catch(LuaError &e) {
						m_server->setAsyncFatalError(e.what());
					}

					EMERGE_DBG_OUT("ended up with: " << analyze_block(block));

					m_server->m_env->activateBlock(block, 0);
				}
			}
		}

		/*
			Set sent status of modified blocks on clients
		*/
		// Add the originally fetched block to the modified list
		if (block)
			modified_blocks[p] = block;

		if (modified_blocks.size() > 0) {
			m_server->SetBlocksNotSent(modified_blocks);
		}
	}
	catch (VersionMismatchException &e) {
		std::ostringstream err;
		。。。
		m_server->setAsyncFatalError(err.str());
	}
	catch (SerializationError &e) {
		std::ostringstream err;
		。。。
		m_server->setAsyncFatalError(err.str());
	}

	log_deregister_thread();
	return NULL;
}
   
   



where启动Emerge线程?

minetest->server.cpp

void Server::AsyncRunStep(bool initial_step) 只有这一处启动,且启动之后,再进入启动会return。

void Server::AsyncRunStep(bool initial_step)

{

。。。

m_emerge->startThreads();

。。。

}




(2)bool popBlockEmerge(v3s16 *pos, u8 *flags)

获取需要生成的block位置以及flagflag是来确定是否可以generate的,有些譬如边界处超出地图范围了是不需要生成的。位置信息是直接从blockqueue队列变量中获得的,每获得一条block位置信息,就将它从queue中删除掉。flag的信息是通过EmergeManager的std::map变量blocks_enqueued 获得的,通过位置获取对应的BlockEmergeData数据,从而得到flag信息。


使用:Thread()方法中最开始的地方使用,获取pos以及flags来进行获取或者生成p处的block。


涉及到的函数:

struct BlockEmergeData 
{
	u16 peer_requested;
	u8 flags;
};

bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags) {
	std::map
   
   
    
    ::iterator iter;
	JMutexAutoLock queuelock(emerge->queuemutex);

	if (blockqueue.empty())
		return false;
	v3s16 p = blockqueue.front();
	blockqueue.pop();

	*pos = p;

	iter = emerge->blocks_enqueued.find(p);
	if (iter == emerge->blocks_enqueued.end())
		return false; //uh oh, queue and map out of sync!!

	BlockEmergeData *bedata = iter->second;
	*flags = bedata->flags;

	emerge->peer_queue_count[bedata->peer_requested]--;

	delete bedata;
	emerge->blocks_enqueued.erase(iter);

	return true;
}

   
   


(3)bool getBlockOrStartGen(v3s16 p, MapBlock **b,BlockMakeData *data, bool allow_generate)

参数:pallow_generate是输入变量,MapBlock **bBlockMakeData *data是输出变量。


这个方法的作用主要是获取位置处的block,如果获取不到,在允许创建的情况下初始化创建一个block出来。先是Load sector,然后是load block,如果block没有load出来,再考虑创建block。因为Mapblock的信息是在MapSector中管理的,而MapSector是在Map中管理的,所以显示加载sector,获取位置p处的MapSector,若没有就加载。然后再load block,是从MapSector获取(内存中),若获取不到,再从硬盘中加载(数据库、sectors文件等)。最后如果MapBlock仍然为空说明还没有这个数据,此时若允许创建生成,则调用ServerMap的创建Block函数(initBlockMake)。



涉及到的函数:


bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b,
									BlockMakeData *data, bool allow_gen) {
	v2s16 p2d(p.X, p.Z);
	//envlock: usually takes <=1ms, sometimes 90ms or ~400ms to acquire
	JMutexAutoLock envlock(m_server->m_env_mutex);

	// Load sector if it isn't loaded
	if (map->getSectorNoGenerateNoEx(p2d) == NULL)
		map->loadSectorMeta(p2d);

	// Attempt to load block
	MapBlock *block = map->getBlockNoCreateNoEx(p);
	if (!block || block->isDummy() || !block->isGenerated()) {
		EMERGE_DBG_OUT("not in memory, attempting to load from disk");
		block = map->loadBlock(p);
		if (block && block->isGenerated())
			map->prepareBlock(block);
	}

	// If could not load and allowed to generate,
	// start generation inside this same envlock
	if (allow_gen && (block == NULL || !block->isGenerated())) {
		EMERGE_DBG_OUT("generating");
		*b = block;
		return map->initBlockMake(data, p);
	}

	*b = block;
	return false;
}


struct BlockMakeData {
	ManualMapVoxelManipulator *vmanip;
	u64 seed;
	v3s16 blockpos_min;
	v3s16 blockpos_max;
	v3s16 blockpos_requested;
	UniqueQueue
   
   
    
     transforming_liquid;
	INodeDefManager *nodedef;

	BlockMakeData():
		vmanip(NULL),
		seed(0),
		nodedef(NULL)
	{}

	~BlockMakeData() { delete vmanip; }
};
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值