diff --git a/src/constants.h b/src/constants.h
index ee08e0ed4ee8503fda43cbbe3dd199a4a45f7dc2..f7ff0aef1488857547b8ee7d5d951c3cf2f69e98 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -28,14 +28,13 @@
 // The absolute working limit is (2^15 - viewing_range).
 #define MAP_GENERATION_LIMIT (31000)
 
-//#define MAX_SIMULTANEOUS_BLOCK_SENDS 7
-//#define MAX_SIMULTANEOUS_BLOCK_SENDS 3
 #define MAX_SIMULTANEOUS_BLOCK_SENDS 2
-//#define MAX_SIMULTANEOUS_BLOCK_SENDS 1
 
 #define FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING 2.0
 #define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 1
 
+#define MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL 4
+
 // Viewing range stuff
 
 #define FPS_DEFAULT_WANTED 30
diff --git a/src/server.cpp b/src/server.cpp
index 8969bdedd9daba9204bacfd16422cfa463015e1e..83d43599ff1447df47c194368553274adb8371f3 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -246,9 +246,11 @@ void * EmergeThread::Thread()
 	return NULL;
 }
 
+#if 0
 void RemoteClient::SendBlocks(Server *server, float dtime)
 {
 	DSTACK(__FUNCTION_NAME);
+	
 	/*
 		Find what blocks to send to the client next, and send them.
 
@@ -518,6 +520,262 @@ void RemoteClient::SendBlocks(Server *server, float dtime)
 
 	// Don't add anything here. The loop breaks by returning.
 }
+#endif // backup of SendBlocks
+
+void RemoteClient::GetNextBlocks(Server *server, float dtime,
+		core::array<PrioritySortedBlockTransfer> &dest)
+{
+	DSTACK(__FUNCTION_NAME);
+	
+	// Won't send anything if already sending
+	{
+		JMutexAutoLock lock(m_blocks_sending_mutex);
+		
+		if(m_blocks_sending.size() >= MAX_SIMULTANEOUS_BLOCK_SENDS)
+		{
+			//dstream<<"Not sending any blocks, Queue full."<<std::endl;
+			return;
+		}
+	}
+
+	Player *player = server->m_env.getPlayer(peer_id);
+
+	v3f playerpos = player->getPosition();
+	v3f playerspeed = player->getSpeed();
+
+	v3s16 center_nodepos = floatToInt(playerpos);
+
+	v3s16 center = getNodeBlockPos(center_nodepos);
+
+	/*
+		Get the starting value of the block finder radius.
+	*/
+	s16 last_nearest_unsent_d;
+	s16 d_start;
+	{
+		JMutexAutoLock lock(m_blocks_sent_mutex);
+		
+		if(m_last_center != center)
+		{
+			m_nearest_unsent_d = 0;
+			m_last_center = center;
+		}
+
+		static float reset_counter = 0;
+		reset_counter += dtime;
+		if(reset_counter > 5.0)
+		{
+			reset_counter = 0;
+			m_nearest_unsent_d = 0;
+		}
+
+		last_nearest_unsent_d = m_nearest_unsent_d;
+		
+		d_start = m_nearest_unsent_d;
+	}
+
+	u16 maximum_simultaneous_block_sends = MAX_SIMULTANEOUS_BLOCK_SENDS;
+
+	{
+		SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
+		m_time_from_building.m_value += dtime;
+		/*
+			Check the time from last addNode/removeNode.
+			Decrease send rate if player is building stuff.
+		*/
+		if(m_time_from_building.m_value
+				< FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
+		{
+			maximum_simultaneous_block_sends
+				= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
+		}
+	}
+
+	// Serialization version used
+	//u8 ser_version = serialization_version;
+
+	//bool has_incomplete_blocks = false;
+	
+	/*
+		TODO: Get this from somewhere
+	*/
+	//s16 d_max = 7;
+	s16 d_max = 8;
+
+	//TODO: Get this from somewhere (probably a bigger value)
+	s16 d_max_gen = 5;
+	
+	//dstream<<"Starting from "<<d_start<<std::endl;
+
+	for(s16 d = d_start; d <= d_max; d++)
+	{
+		//dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
+		
+		//if(has_incomplete_blocks == false)
+		{
+			JMutexAutoLock lock(m_blocks_sent_mutex);
+			/*
+				If m_nearest_unsent_d was changed by the EmergeThread
+				(it can change it to 0 through SetBlockNotSent),
+				update our d to it.
+				Else update m_nearest_unsent_d
+			*/
+			if(m_nearest_unsent_d != last_nearest_unsent_d)
+			{
+				d = m_nearest_unsent_d;
+			}
+			else
+			{
+				m_nearest_unsent_d = d;
+			}
+			last_nearest_unsent_d = m_nearest_unsent_d;
+		}
+
+		/*
+			Get the border/face dot coordinates of a "d-radiused"
+			box
+		*/
+		core::list<v3s16> list;
+		getFacePositions(list, d);
+		
+		core::list<v3s16>::Iterator li;
+		for(li=list.begin(); li!=list.end(); li++)
+		{
+			v3s16 p = *li + center;
+			
+			/*
+				Send throttling
+				- Don't allow too many simultaneous transfers
+
+				Also, don't send blocks that are already flying.
+			*/
+			{
+				JMutexAutoLock lock(m_blocks_sending_mutex);
+				
+				// Limit is dynamically lowered when building
+				if(m_blocks_sending.size()
+						>= maximum_simultaneous_block_sends)
+				{
+					/*dstream<<"Not sending more blocks. Queue full. "
+							<<m_blocks_sending.size()
+							<<std::endl;*/
+					return;
+				}
+
+				if(m_blocks_sending.find(p) != NULL)
+					continue;
+			}
+			
+			/*
+				Do not go 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;
+
+			bool generate = d <= d_max_gen;
+		
+			// Limit the generating area vertically to half
+			if(abs(p.Y - center.Y) > d_max_gen / 2)
+				generate = false;
+			
+			/*
+				Don't send already sent blocks
+			*/
+			{
+				JMutexAutoLock lock(m_blocks_sent_mutex);
+				
+				if(m_blocks_sent.find(p) != NULL)
+					continue;
+			}
+					
+			/*
+				Check if map has this block
+			*/
+			MapBlock *block = NULL;
+			try
+			{
+				block = server->m_env.getMap().getBlockNoCreate(p);
+			}
+			catch(InvalidPositionException &e)
+			{
+			}
+			
+			bool surely_not_found_on_disk = false;
+			if(block != NULL)
+			{
+				/*if(block->isIncomplete())
+				{
+					has_incomplete_blocks = true;
+					continue;
+				}*/
+
+				if(block->isDummy())
+				{
+					surely_not_found_on_disk = true;
+				}
+			}
+
+			/*
+				If block has been marked to not exist on disk (dummy)
+				and generating new ones is not wanted, skip block. TODO
+			*/
+			if(generate == false && surely_not_found_on_disk == true)
+			{
+				// get next one.
+				continue;
+			}
+
+			/*
+				Add inexistent block to emerge queue.
+			*/
+			if(block == NULL || surely_not_found_on_disk)
+			{
+				// Block not found.
+				SharedPtr<JMutexAutoLock> lock
+						(m_num_blocks_in_emerge_queue.getLock());
+				
+				//TODO: Get value from somewhere
+				//TODO: Balance between clients
+				//if(server->m_emerge_queue.size() < 1)
+
+				// Allow only one block in emerge queue
+				if(m_num_blocks_in_emerge_queue.m_value == 0)
+				{
+					// Add it to the emerge queue and trigger the thread
+					
+					u8 flags = 0;
+					if(generate == false)
+						flags |= TOSERVER_GETBLOCK_FLAG_OPTIONAL;
+					
+					{
+						m_num_blocks_in_emerge_queue.m_value++;
+					}
+
+					server->m_emerge_queue.addBlock(peer_id, p, flags);
+					server->m_emergethread.trigger();
+				}
+				
+				// get next one.
+				continue;
+			}
+
+			/*
+				Add block to queue
+			*/
+
+			PrioritySortedBlockTransfer q((float)d, p, peer_id);
+
+			dest.push_back(q);
+		}
+	}
+
+	// Don't add anything here. The loop breaks by returning.
+}
 
 void RemoteClient::SendObjectData(
 		Server *server,
@@ -2069,6 +2327,7 @@ void Server::SendInventory(u16 peer_id)
 	m_con.Send(peer_id, 0, data, true);
 }
 
+#if 0
 void Server::SendBlocks(float dtime)
 {
 	DSTACK(__FUNCTION_NAME);
@@ -2095,6 +2354,68 @@ void Server::SendBlocks(float dtime)
 
 	//dstream<<"Server::SendBlocks(): END"<<std::endl;
 }
+#endif
+
+void Server::SendBlocks(float dtime)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	JMutexAutoLock envlock(m_env_mutex);
+
+	core::array<PrioritySortedBlockTransfer> queue;
+
+	s32 total_sending = 0;
+
+	for(core::map<u16, RemoteClient*>::Iterator
+		i = m_clients.getIterator();
+		i.atEnd() == false; i++)
+	{
+		RemoteClient *client = i.getNode()->getValue();
+		assert(client->peer_id == i.getNode()->getKey());
+
+		total_sending += client->SendingCount();
+		
+		if(client->serialization_version == SER_FMT_VER_INVALID)
+			continue;
+		
+		client->GetNextBlocks(this, dtime, queue);
+	}
+
+	// Sort.
+	// Lowest priority number comes first.
+	// Lowest is most important.
+	queue.sort();
+
+	JMutexAutoLock conlock(m_con_mutex);
+
+	for(u32 i=0; i<queue.size(); i++)
+	{
+		//TODO: Calculate value dynamically
+		if(total_sending >= MAX_SIMULTANEOUS_BLOCK_SENDS_SERVER_TOTAL)
+			break;
+		
+		PrioritySortedBlockTransfer q = queue[i];
+
+		MapBlock *block = NULL;
+		try
+		{
+			block = m_env.getMap().getBlockNoCreate(q.pos);
+		}
+		catch(InvalidPositionException &e)
+		{
+			continue;
+		}
+
+		RemoteClient *client = getClient(q.peer_id);
+
+		SendBlockNoLock(q.peer_id, block, client->serialization_version);
+
+		client->SentBlock(q.pos);
+
+		total_sending++;
+	}
+}
+
 
 RemoteClient* Server::getClient(u16 peer_id)
 {
diff --git a/src/server.h b/src/server.h
index c178480d59e3455ef7aeaf8e92be97088f4da1ad..8e2e0562603846964c971e3f807ee63b66efb7f6 100644
--- a/src/server.h
+++ b/src/server.h
@@ -208,11 +208,11 @@ u32 PIChecksum(core::list<PlayerInfo> &l);
 */
 struct PrioritySortedBlockTransfer
 {
-	PrioritySortedBlockTransfer(float a_priority, v3s16 a_pos, u16 a_dest_peer)
+	PrioritySortedBlockTransfer(float a_priority, v3s16 a_pos, u16 a_peer_id)
 	{
 		priority = a_priority;
 		pos = a_pos;
-		dest_peer = a_dest_peer;
+		peer_id = a_peer_id;
 	}
 	bool operator < (PrioritySortedBlockTransfer &other)
 	{
@@ -220,7 +220,7 @@ struct PrioritySortedBlockTransfer
 	}
 	float priority;
 	v3s16 pos;
-	u16 a_dest_peer;
+	u16 peer_id;
 };
 
 class RemoteClient
@@ -252,8 +252,13 @@ class RemoteClient
 	{
 	}
 	
-	// Connection and environment should be locked when this is called
-	void SendBlocks(Server *server, float dtime);
+	/*
+		Finds block that should be sent next to the client.
+		Environment should be locked when this is called.
+		dtime is used for resetting send radius at slow interval
+	*/
+	void GetNextBlocks(Server *server, float dtime,
+			core::array<PrioritySortedBlockTransfer> &dest);
 
 	// Connection and environment should be locked when this is called
 	// steps() objects of blocks not found in active_blocks, then
@@ -272,6 +277,18 @@ class RemoteClient
 	void SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks);
 
 	void BlockEmerged();
+
+	/*bool IsSendingBlock(v3s16 p)
+	{
+		JMutexAutoLock lock(m_blocks_sending_mutex);
+		return (m_blocks_sending.find(p) != NULL);
+	}*/
+
+	s32 SendingCount()
+	{
+		JMutexAutoLock lock(m_blocks_sending_mutex);
+		return m_blocks_sending.size();
+	}
 	
 	// Increments timeouts and removes timed-out blocks from list
 	// NOTE: This doesn't fix the server-not-sending-block bug