Map generation is now properly threaded and doesn't block block placement and other...
authorPerttu Ahola <celeron55@gmail.com>
Sun, 10 Apr 2011 17:18:34 +0000 (20:18 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 10 Apr 2011 17:18:34 +0000 (20:18 +0300)
src/environment.cpp
src/map.cpp
src/map.h
src/server.cpp

index d2fb8a39d2d8978a3c0659e2df35f5e3beadeb98..77746c5bea1157aa759a043d72b89951e5661f5d 100644 (file)
@@ -429,7 +429,7 @@ void ServerEnvironment::step(float dtime)
 
        bool send_recommended = false;
        m_send_recommended_timer += dtime;
-       if(m_send_recommended_timer > 0.1)
+       if(m_send_recommended_timer > 0.15)
        {
                m_send_recommended_timer = 0;
                send_recommended = true;
index 334de40defc41fc437c20955d1910d2fbcdacaca..8d79ae3669b924c69e9fc95b56feec08000ad82d 100644 (file)
@@ -2170,23 +2170,6 @@ void addRandomObjects(MapBlock *block)
        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)
 {
        s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
@@ -2235,11 +2218,11 @@ void makeChunk(ChunkMakeData *data)
                /*
                        Skip of already generated
                */
-               {
+               /*{
                        v3s16 p(p2d.X, y_nodes_min, p2d.Y);
                        if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
                                continue;
-               }
+               }*/
 
                // Ground height at this point
                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));
                        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_area.add_y(em, i, 1);
@@ -3426,33 +3416,8 @@ void makeChunk(ChunkMakeData *data)
 //###################################################################
 //###################################################################
 
-MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
-               core::map<v3s16, MapBlock*> &changed_blocks,
-               bool force)
+void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
 {
-       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.
        s16 max_spread_amount_sectors = 2;
        assert(max_spread_amount_sectors <= m_chunksize);
@@ -3469,8 +3434,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        s16 sectorpos_bigbase_size =
                        sectorpos_base_size + 2 * max_spread_amount_sectors;
                        
-       ChunkMakeData data;
        data.seed = m_seed;
+       data.chunkpos = chunkpos;
        data.y_blocks_min = y_blocks_min;
        data.y_blocks_max = y_blocks_max;
        data.sectorpos_base = sectorpos_base;
@@ -3541,9 +3506,11 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                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
        */
@@ -3569,14 +3536,14 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                Add random objects to blocks
        */
        {
-               for(s16 x=0; x<sectorpos_base_size; x++)
-               for(s16 z=0; z<sectorpos_base_size; z++)
+               for(s16 x=0; x<data.sectorpos_base_size; x++)
+               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);
                        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);
                                MapBlock *block = createBlock(blockpos);
@@ -3592,7 +3559,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        for(s16 x=-1; x<=1; x++)
        for(s16 y=-1; y<=1; y++)
        {
-               v2s16 chunkpos0 = chunkpos + v2s16(x,y);
+               v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
                // Add chunk meta information
                MapChunk *chunk = getChunk(chunkpos0);
                if(chunk == NULL)
@@ -3608,7 +3575,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        /*
                Set central chunk non-volatile
        */
-       MapChunk *chunk = getChunk(chunkpos);
+       MapChunk *chunk = getChunk(data.chunkpos);
        assert(chunk);
        // Set non-volatile
        //chunk->setIsVolatile(false);
@@ -3618,6 +3585,48 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                Save changed parts of map
        */
        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)
@@ -3625,6 +3634,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        return chunk;
 }
 
+// NOTE: Deprecated
 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
                core::map<v3s16, MapBlock*> &changed_blocks)
 {
index 8a004ee64b5337a3387bcd3b9db834dc10297522..e2cd432be381f9a604af70f9f2ab4549d077d25e 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -318,6 +318,8 @@ protected:
        This is the only map class that is able to generate map.
 */
 
+struct ChunkMakeData;
+
 class ServerMap : public Map
 {
 public:
@@ -391,6 +393,10 @@ public:
                return true;
        }
 
+       void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
+       MapChunk* finishChunkMake(ChunkMakeData &data,
+                       core::map<v3s16, MapBlock*> &changed_blocks);
+
        /*
                Generate a chunk.
 
@@ -746,5 +752,25 @@ protected:
        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
 
index c5703bf334324211b9aa9e5af2f003148cabfe47..63d8e31db786c809e14bc5323217f4ef93edbcd8 100644 (file)
@@ -72,7 +72,7 @@ void * EmergeThread::Thread()
 
        DSTACK(__FUNCTION_NAME);
 
-       bool debug=false;
+       //bool debug=false;
        
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
@@ -91,7 +91,19 @@ void * EmergeThread::Thread()
                SharedPtr<QueuedBlockEmerge> q(qptr);
 
                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;
 
                //TimeTaker timer("block emerge");
@@ -144,78 +156,67 @@ void * EmergeThread::Thread()
                if(optional)
                        only_from_disk = true;
 
-               /*
-                       TODO: Map loading logic here, so that the chunk can be
-                       generated asynchronously:
-
-                       - Check limits
-                       With the environment locked:
-                       - Check if block already is loaded and not dummy
-                               - If so, we're ready
-                       - 
-               */
-               
-               {//envlock
-
-               //TimeTaker envlockwaittimer("block emerge envlock wait time");
-               
-               // 0-50ms
-               JMutexAutoLock envlock(m_server->m_env_mutex);
-
-               //envlockwaittimer.stop();
+               v2s16 chunkpos = map.sector_to_chunk(p2d);
 
-               //TimeTaker timer("block emerge (while env locked)");
-                       
-               try{
+               bool generate_chunk = false;
+               if(only_from_disk == false)
+               {
+                       JMutexAutoLock envlock(m_server->m_env_mutex);
+                       if(map.chunkNonVolatile(chunkpos) == false)
+                               generate_chunk = true;
+               }
+               if(generate_chunk)
+               {
+                       ChunkMakeData data;
                        
-                       // First check if the block already exists
-                       //block = map.getBlockNoCreate(p);
-
-                       if(block == NULL)
                        {
-                               //dstream<<"Calling emergeBlock"<<std::endl;
-                               block = map.emergeBlock(
-                                               p,
-                                               only_from_disk,
-                                               changed_blocks,
-                                               lighting_invalidated_blocks);
+                               JMutexAutoLock envlock(m_server->m_env_mutex);
+                               map.initChunkMake(data, chunkpos);
                        }
 
-                       // If it is a dummy, block was not found on disk
-                       if(block->isDummy())
-                       {
-                               //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
-                               got_block = false;
+                       makeChunk(&data);
 
-                               if(only_from_disk == false)
-                               {
-                                       dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
-                                       assert(0);
-                               }
+                       {
+                               JMutexAutoLock envlock(m_server->m_env_mutex);
+                               map.finishChunkMake(data, changed_blocks);
                        }
                }
-               catch(InvalidPositionException &e)
-               {
-                       // Block not found.
-                       // This happens when position is over limit.
-                       got_block = false;
-               }
-               
-               if(got_block)
+       
+               /*
+                       Fetch block from map or generate a single block
+               */
                {
-                       if(debug && changed_blocks.size() > 0)
+                       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())
                        {
-                               dout_server<<DTIME<<"Got changed_blocks: ";
-                               for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
-                                               i.atEnd() == false; i++)
+                               if(only_from_disk)
+                               {
+                                       got_block = false;
+                               }
+                               else
                                {
-                                       MapBlock *block = i.getNode()->getValue();
-                                       v3s16 p = block->getPos();
-                                       dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
+                                       ServerMapSector *sector =
+                                                       (ServerMapSector*)map.getSectorNoGenerateNoEx(p2d);
+                                       block = map.generateBlock(p, block, sector, changed_blocks,
+                                                       lighting_invalidated_blocks);
                                }
-                               dout_server<<std::endl;
                        }
 
+                       // TODO: Some additional checking and lighting updating,
+                       // see emergeBlock
+               }
+
+               {//envlock
+               JMutexAutoLock envlock(m_server->m_env_mutex);
+               
+               if(got_block)
+               {
                        /*
                                Collect a list of blocks that have been modified in
                                addition to the fetched one.