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
从数据文件中加载。