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)来生成block,finishBlockMake将数据设置到地图上,即设置到对应的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,管理的x和z固定下的所有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)来生成block,finishBlockMake将数据设置到地图上,即设置到对应的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),data是MapBlock中MapNode *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) 中调用ServeMap的save函数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(¶ms);
void ServerMap::loadMapMeta()
minetest->emerge.cpp
m_emerge->loadParamsFromSettings(¶ms);
void ServerMap::saveBlock(MapBlock *block)
将block数据保存到数据库文件中。
void ServerMap::loadBlock
从数据文件中加载。