diff --git a/src/environment.cpp b/src/environment.cpp
index d2fb8a39d2d8978a3c0659e2df35f5e3beadeb98..77746c5bea1157aa759a043d72b89951e5661f5d 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -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;
diff --git a/src/map.cpp b/src/map.cpp
index 334de40defc41fc437c20955d1910d2fbcdacaca..8d79ae3669b924c69e9fc95b56feec08000ad82d 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -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)
 {
diff --git a/src/map.h b/src/map.h
index 8a004ee64b5337a3387bcd3b9db834dc10297522..e2cd432be381f9a604af70f9f2ab4549d077d25e 100644
--- a/src/map.h
+++ b/src/map.h
@@ -318,6 +318,8 @@ class Map : public NodeContainer
 	This is the only map class that is able to generate map.
 */
 
+struct ChunkMakeData;
+
 class ServerMap : public Map
 {
 public:
@@ -391,6 +393,10 @@ class ServerMap : public Map
 		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 @@ class ManualMapVoxelManipulator : public MapVoxelManipulator
 	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
 
diff --git a/src/server.cpp b/src/server.cpp
index c5703bf334324211b9aa9e5af2f003148cabfe47..63d8e31db786c809e14bc5323217f4ef93edbcd8 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -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.