Map generation is now properly threaded and doesn't block block placement and other stuff.

This commit is contained in:
Perttu Ahola 2011-04-10 20:18:34 +03:00
parent 6fa85c8502
commit 10eedbc1d2
4 changed files with 153 additions and 116 deletions

View File

@ -429,7 +429,7 @@ void ServerEnvironment::step(float dtime)
bool send_recommended = false; bool send_recommended = false;
m_send_recommended_timer += dtime; m_send_recommended_timer += dtime;
if(m_send_recommended_timer > 0.1) if(m_send_recommended_timer > 0.15)
{ {
m_send_recommended_timer = 0; m_send_recommended_timer = 0;
send_recommended = true; send_recommended = true;

View File

@ -2170,23 +2170,6 @@ void addRandomObjects(MapBlock *block)
This is the main map generation method This is the main map generation method
*/ */
struct ChunkMakeData
{
ManualMapVoxelManipulator vmanip;
u64 seed;
s16 y_blocks_min;
s16 y_blocks_max;
v2s16 sectorpos_base;
s16 sectorpos_base_size;
v2s16 sectorpos_bigbase;
s16 sectorpos_bigbase_size;
s16 max_spread_amount;
ChunkMakeData():
vmanip(NULL)
{}
};
void makeChunk(ChunkMakeData *data) void makeChunk(ChunkMakeData *data)
{ {
s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE; s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
@ -2235,11 +2218,11 @@ void makeChunk(ChunkMakeData *data)
/* /*
Skip of already generated Skip of already generated
*/ */
{ /*{
v3s16 p(p2d.X, y_nodes_min, p2d.Y); v3s16 p(p2d.X, y_nodes_min, p2d.Y);
if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR) if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
continue; continue;
} }*/
// Ground height at this point // Ground height at this point
float surface_y_f = 0.0; float surface_y_f = 0.0;
@ -2270,6 +2253,13 @@ void makeChunk(ChunkMakeData *data)
u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y)); u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++) for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
{ {
// Skip if already generated.
// This is done here because there might be a cave at
// any point in ground, which could look like it
// wasn't generated.
if(data->vmanip.m_data[i].d != CONTENT_AIR)
break;
data->vmanip.m_data[i].d = CONTENT_STONE; data->vmanip.m_data[i].d = CONTENT_STONE;
data->vmanip.m_area.add_y(em, i, 1); data->vmanip.m_area.add_y(em, i, 1);
@ -3426,33 +3416,8 @@ void makeChunk(ChunkMakeData *data)
//################################################################### //###################################################################
//################################################################### //###################################################################
MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
core::map<v3s16, MapBlock*> &changed_blocks,
bool force)
{ {
DSTACK(__FUNCTION_NAME);
/*
Don't generate if already fully generated
*/
if(force == false)
{
MapChunk *chunk = getChunk(chunkpos);
if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
{
dstream<<"generateChunkRaw(): Chunk "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<" already generated"<<std::endl;
return chunk;
}
}
dstream<<"generateChunkRaw(): Generating chunk "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<std::endl;
TimeTaker timer("generateChunkRaw()");
// The distance how far into the neighbors the generator is allowed to go. // The distance how far into the neighbors the generator is allowed to go.
s16 max_spread_amount_sectors = 2; s16 max_spread_amount_sectors = 2;
assert(max_spread_amount_sectors <= m_chunksize); assert(max_spread_amount_sectors <= m_chunksize);
@ -3469,8 +3434,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
s16 sectorpos_bigbase_size = s16 sectorpos_bigbase_size =
sectorpos_base_size + 2 * max_spread_amount_sectors; sectorpos_base_size + 2 * max_spread_amount_sectors;
ChunkMakeData data;
data.seed = m_seed; data.seed = m_seed;
data.chunkpos = chunkpos;
data.y_blocks_min = y_blocks_min; data.y_blocks_min = y_blocks_min;
data.y_blocks_max = y_blocks_max; data.y_blocks_max = y_blocks_max;
data.sectorpos_base = sectorpos_base; data.sectorpos_base = sectorpos_base;
@ -3541,9 +3506,11 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max); data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
} }
// Generate stuff }
makeChunk(&data);
MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
core::map<v3s16, MapBlock*> &changed_blocks)
{
/* /*
Blit generated stuff to map Blit generated stuff to map
*/ */
@ -3569,14 +3536,14 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
Add random objects to blocks Add random objects to blocks
*/ */
{ {
for(s16 x=0; x<sectorpos_base_size; x++) for(s16 x=0; x<data.sectorpos_base_size; x++)
for(s16 z=0; z<sectorpos_base_size; z++) for(s16 z=0; z<data.sectorpos_base_size; z++)
{ {
v2s16 sectorpos = sectorpos_base + v2s16(x,z); v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
ServerMapSector *sector = createSector(sectorpos); ServerMapSector *sector = createSector(sectorpos);
assert(sector); assert(sector);
for(s16 y=y_blocks_min; y<=y_blocks_max; y++) for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
{ {
v3s16 blockpos(sectorpos.X, y, sectorpos.Y); v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
MapBlock *block = createBlock(blockpos); MapBlock *block = createBlock(blockpos);
@ -3592,7 +3559,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
for(s16 x=-1; x<=1; x++) for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++) for(s16 y=-1; y<=1; y++)
{ {
v2s16 chunkpos0 = chunkpos + v2s16(x,y); v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
// Add chunk meta information // Add chunk meta information
MapChunk *chunk = getChunk(chunkpos0); MapChunk *chunk = getChunk(chunkpos0);
if(chunk == NULL) if(chunk == NULL)
@ -3608,7 +3575,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
/* /*
Set central chunk non-volatile Set central chunk non-volatile
*/ */
MapChunk *chunk = getChunk(chunkpos); MapChunk *chunk = getChunk(data.chunkpos);
assert(chunk); assert(chunk);
// Set non-volatile // Set non-volatile
//chunk->setIsVolatile(false); //chunk->setIsVolatile(false);
@ -3618,6 +3585,48 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
Save changed parts of map Save changed parts of map
*/ */
save(true); save(true);
return chunk;
}
// NOTE: Deprecated
MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
core::map<v3s16, MapBlock*> &changed_blocks,
bool force)
{
DSTACK(__FUNCTION_NAME);
/*
Don't generate if already fully generated
*/
if(force == false)
{
MapChunk *chunk = getChunk(chunkpos);
if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
{
dstream<<"generateChunkRaw(): Chunk "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<" already generated"<<std::endl;
return chunk;
}
}
dstream<<"generateChunkRaw(): Generating chunk "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<std::endl;
TimeTaker timer("generateChunkRaw()");
ChunkMakeData data;
// Initialize generation
initChunkMake(data, chunkpos);
// Generate stuff
makeChunk(&data);
// Finalize generation
MapChunk *chunk = finishChunkMake(data, changed_blocks);
/* /*
Return central chunk (which was requested) Return central chunk (which was requested)
@ -3625,6 +3634,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
return chunk; return chunk;
} }
// NOTE: Deprecated
MapChunk* ServerMap::generateChunk(v2s16 chunkpos1, MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
core::map<v3s16, MapBlock*> &changed_blocks) core::map<v3s16, MapBlock*> &changed_blocks)
{ {

View File

@ -318,6 +318,8 @@ protected:
This is the only map class that is able to generate map. This is the only map class that is able to generate map.
*/ */
struct ChunkMakeData;
class ServerMap : public Map class ServerMap : public Map
{ {
public: public:
@ -391,6 +393,10 @@ public:
return true; return true;
} }
void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
MapChunk* finishChunkMake(ChunkMakeData &data,
core::map<v3s16, MapBlock*> &changed_blocks);
/* /*
Generate a chunk. Generate a chunk.
@ -746,5 +752,25 @@ protected:
bool m_create_area; bool m_create_area;
}; };
struct ChunkMakeData
{
ManualMapVoxelManipulator vmanip;
u64 seed;
v2s16 chunkpos;
s16 y_blocks_min;
s16 y_blocks_max;
v2s16 sectorpos_base;
s16 sectorpos_base_size;
v2s16 sectorpos_bigbase;
s16 sectorpos_bigbase_size;
s16 max_spread_amount;
ChunkMakeData():
vmanip(NULL)
{}
};
void makeChunk(ChunkMakeData *data);
#endif #endif

View File

@ -72,7 +72,7 @@ void * EmergeThread::Thread()
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
bool debug=false; //bool debug=false;
BEGIN_DEBUG_EXCEPTION_HANDLER BEGIN_DEBUG_EXCEPTION_HANDLER
@ -91,7 +91,19 @@ void * EmergeThread::Thread()
SharedPtr<QueuedBlockEmerge> q(qptr); SharedPtr<QueuedBlockEmerge> q(qptr);
v3s16 &p = q->pos; v3s16 &p = q->pos;
v2s16 p2d(p.X,p.Z);
/*
Do not generate over-limit
*/
if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
continue;
//derr_server<<"EmergeThread::Thread(): running"<<std::endl; //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
//TimeTaker timer("block emerge"); //TimeTaker timer("block emerge");
@ -144,78 +156,67 @@ void * EmergeThread::Thread()
if(optional) if(optional)
only_from_disk = true; only_from_disk = true;
/* v2s16 chunkpos = map.sector_to_chunk(p2d);
TODO: Map loading logic here, so that the chunk can be
generated asynchronously:
- Check limits bool generate_chunk = false;
With the environment locked: if(only_from_disk == false)
- Check if block already is loaded and not dummy {
- If so, we're ready JMutexAutoLock envlock(m_server->m_env_mutex);
- if(map.chunkNonVolatile(chunkpos) == false)
*/ generate_chunk = true;
}
{//envlock if(generate_chunk)
{
//TimeTaker envlockwaittimer("block emerge envlock wait time"); ChunkMakeData data;
// 0-50ms
JMutexAutoLock envlock(m_server->m_env_mutex);
//envlockwaittimer.stop();
//TimeTaker timer("block emerge (while env locked)");
try{
// First check if the block already exists
//block = map.getBlockNoCreate(p);
if(block == NULL)
{ {
//dstream<<"Calling emergeBlock"<<std::endl; JMutexAutoLock envlock(m_server->m_env_mutex);
block = map.emergeBlock( map.initChunkMake(data, chunkpos);
p,
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
} }
// If it is a dummy, block was not found on disk makeChunk(&data);
if(block->isDummy())
{
//dstream<<"EmergeThread: Got a dummy block"<<std::endl;
got_block = false;
if(only_from_disk == false) {
JMutexAutoLock envlock(m_server->m_env_mutex);
map.finishChunkMake(data, changed_blocks);
}
}
/*
Fetch block from map or generate a single block
*/
{
JMutexAutoLock envlock(m_server->m_env_mutex);
// Load sector if it isn't loaded
if(map.getSectorNoGenerateNoEx(p2d) == NULL)
map.loadSectorFull(p2d);
block = map.getBlockNoCreateNoEx(p);
if(!block || block->isDummy())
{
if(only_from_disk)
{ {
dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl; got_block = false;
assert(0); }
else
{
ServerMapSector *sector =
(ServerMapSector*)map.getSectorNoGenerateNoEx(p2d);
block = map.generateBlock(p, block, sector, changed_blocks,
lighting_invalidated_blocks);
} }
} }
// TODO: Some additional checking and lighting updating,
// see emergeBlock
} }
catch(InvalidPositionException &e)
{ {//envlock
// Block not found. JMutexAutoLock envlock(m_server->m_env_mutex);
// This happens when position is over limit.
got_block = false;
}
if(got_block) if(got_block)
{ {
if(debug && changed_blocks.size() > 0)
{
dout_server<<DTIME<<"Got changed_blocks: ";
for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos();
dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
}
dout_server<<std::endl;
}
/* /*
Collect a list of blocks that have been modified in Collect a list of blocks that have been modified in
addition to the fetched one. addition to the fetched one.