diff --git a/src/client.cpp b/src/client.cpp
index d43b97a3ceb1952188de3738b05cdd316ba7f4c3..af2b375f48dbf52d5f81f72ce38e07c5c9d358bb 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -66,14 +66,9 @@ void * ClientUpdateThread::Thread()
 Client::Client(
 		IrrlichtDevice *device,
 		const char *playername,
-		JMutex &range_mutex,
-		float &viewing_range_nodes,
-		bool &viewing_range_all):
+		MapDrawControl &control):
 	m_thread(this),
-	m_env(new ClientMap(this,
-			range_mutex,
-			viewing_range_nodes,
-			viewing_range_all,
+	m_env(new ClientMap(this, control,
 			device->getSceneManager()->getRootSceneNode(),
 			device->getSceneManager(), 666),
 			dout_client),
diff --git a/src/client.h b/src/client.h
index e48c8ec69e2aa6deae433e0a8631adfad14d9eb8..c56b615cb6d86edb08dcd3684ce1a2a2ee20ae3a 100644
--- a/src/client.h
+++ b/src/client.h
@@ -110,9 +110,7 @@ class Client : public con::PeerHandler
 	Client(
 			IrrlichtDevice *device,
 			const char *playername,
-			JMutex &range_mutex,
-			float &viewing_range_nodes,
-			bool &viewing_range_all
+			MapDrawControl &control
 			);
 	
 	~Client();
diff --git a/src/main.cpp b/src/main.cpp
index b7a1b6b328c5f3fa625c30f3bb86ec22e599d278..2f22caeb011b499cc5fe076eba5d6048cd426154 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -253,25 +253,7 @@ TODO: Fix viewing range updater's oscillation when there is large non-
 
 IrrlichtWrapper *g_irrlicht;
 
-// All range-related stuff below is locked behind this
-JMutex g_range_mutex;
-
-// Blocks are viewed in this range from the player
-float g_viewing_range_nodes = 60;
-//s16 g_viewing_range_nodes = 0;
-
-// This is updated by the client's fetchBlocks routine
-//s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
-
-// If true, the preceding value has no meaning and all blocks
-// already existing in memory are drawn
-bool g_viewing_range_all = false;
-
-// This is the freetime ratio imposed by the dynamic viewing
-// range changing code.
-// It is controlled by the main loop to the smallest value that
-// inhibits glitches (dtime jitter) in the main loop.
-//float g_freetime_ratio = FREETIME_RATIO_MAX;
+MapDrawControl draw_control;
 
 /*
 	Settings.
@@ -455,15 +437,14 @@ class MyEventReceiver : public IEventReceiver
 				// Viewing range selection
 				if(event.KeyInput.Key == irr::KEY_KEY_R)
 				{
-					JMutexAutoLock lock(g_range_mutex);
-					if(g_viewing_range_all)
+					if(draw_control.range_all)
 					{
-						g_viewing_range_all = false;
+						draw_control.range_all = false;
 						dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
 					}
 					else
 					{
-						g_viewing_range_all = true;
+						draw_control.range_all = true;
 						dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
 					}
 				}
@@ -853,7 +834,7 @@ class RandomInputHandler : public InputHandler
 void updateViewingRange(f32 frametime, Client *client)
 {
 	// Range_all messes up frametime_avg
-	if(g_viewing_range_all == true)
+	if(draw_control.range_all == true)
 		return;
 
 	float wanted_fps = g_settings.getFloat("wanted_fps");
@@ -887,7 +868,7 @@ void updateViewingRange(f32 frametime, Client *client)
 
 	float fraction_unbiased = frametime_avg / frametime_wanted;
 
-	float fraction = pow(fraction_unbiased, 20./(float)g_viewing_range_nodes);
+	float fraction = pow(fraction_unbiased, 20./(float)draw_control.wanted_range);
 	
 	/*float fraction = 1.0;
 	// If frametime is too high
@@ -948,7 +929,7 @@ void updateViewingRange(f32 frametime, Client *client)
 	s16 viewing_range_nodes_min = g_settings.getS16("viewing_range_nodes_min");
 	s16 viewing_range_nodes_max = g_settings.getS16("viewing_range_nodes_max");
 
-	s16 n = (float)g_viewing_range_nodes / fraction;
+	s16 n = (float)draw_control.wanted_range / fraction;
 	if(n < viewing_range_nodes_min)
 		n = viewing_range_nodes_min;
 	if(n > viewing_range_nodes_max)
@@ -956,20 +937,20 @@ void updateViewingRange(f32 frametime, Client *client)
 
 	bool can_change = true;
 
-	if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
+	if(client->isFetchingBlocks() == true && n > draw_control.wanted_range)
 		can_change = false;
 	
 	if(can_change)
-		g_viewing_range_nodes = n;
+		draw_control.wanted_range = n;
 
-	/*dstream<<"g_viewing_range_nodes = "
-			<<g_viewing_range_nodes<<std::endl;*/
+	/*dstream<<"draw_control.wanted_range = "
+			<<draw_control.wanted_range<<std::endl;*/
 }
 #endif
 
 void updateViewingRange(f32 frametime_in, Client *client)
 {
-	if(g_viewing_range_all == true)
+	if(draw_control.range_all == true)
 		return;
 	
 	static f32 added_frametime = 0;
@@ -990,7 +971,29 @@ void updateViewingRange(f32 frametime_in, Client *client)
 			<<": Collected "<<added_frames<<" frames, total of "
 			<<added_frametime<<"s."<<std::endl;
 	
-	f32 frametime = added_frametime / added_frames;
+	dstream<<"draw_control.blocks_drawn="
+			<<draw_control.blocks_drawn
+			<<", draw_control.blocks_would_have_drawn="
+			<<draw_control.blocks_would_have_drawn
+			<<std::endl;
+	
+	float range_min = g_settings.getS16("viewing_range_nodes_min");
+	float range_max = g_settings.getS16("viewing_range_nodes_max");
+	
+	draw_control.wanted_min_range = range_min;
+	draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
+	
+	float block_draw_ratio = 1.0;
+	if(draw_control.blocks_would_have_drawn != 0)
+	{
+		block_draw_ratio = (float)draw_control.blocks_drawn
+			/ (float)draw_control.blocks_would_have_drawn;
+	}
+
+	// Calculate the average frametime in the case that all wanted
+	// blocks had been drawn
+	f32 frametime = added_frametime / added_frames / block_draw_ratio;
+	
 	added_frametime = 0.0;
 	added_frames = 0;
 	
@@ -1007,7 +1010,7 @@ void updateViewingRange(f32 frametime_in, Client *client)
 		return;
 	}
 
-	float range = g_viewing_range_nodes;
+	float range = draw_control.wanted_range;
 	float new_range = range;
 
 	static s16 range_old = 0;
@@ -1029,16 +1032,24 @@ void updateViewingRange(f32 frametime_in, Client *client)
 	// A high value here results in slow changing range (0.0025)
 	// SUGG: This could be dynamically adjusted so that when
 	//       the camera is turning, this is lower
-	float min_time_per_range = 0.001;
+	//float min_time_per_range = 0.0015;
+	float min_time_per_range = 0.0010;
+	//float min_time_per_range = 0.05 / range;
 	if(time_per_range < min_time_per_range)
+	{
 		time_per_range = min_time_per_range;
-	
-	dstream<<"time_per_range="<<time_per_range<<std::endl;
+		dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
+	}
+	else
+	{
+		dstream<<"time_per_range="<<time_per_range<<std::endl;
+	}
 
 	f32 wanted_range_change = wanted_frametime_change / time_per_range;
 	// Dampen the change a bit to kill oscillations
 	//wanted_range_change *= 0.9;
-	wanted_range_change *= 0.75;
+	//wanted_range_change *= 0.75;
+	wanted_range_change *= 0.5;
 	dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
 
 	// If needed range change is very small, just return
@@ -1051,9 +1062,6 @@ void updateViewingRange(f32 frametime_in, Client *client)
 	new_range += wanted_range_change;
 	dstream<<"new_range="<<new_range/*<<std::endl*/;
 	
-	float range_min = g_settings.getS16("viewing_range_nodes_min");
-	float range_max = g_settings.getS16("viewing_range_nodes_max");
-	
 	float new_range_unclamped = new_range;
 	if(new_range < range_min)
 		new_range = range_min;
@@ -1065,9 +1073,7 @@ void updateViewingRange(f32 frametime_in, Client *client)
 	else
 		dstream<<std::endl;
 
-	JMutexAutoLock lock(g_range_mutex);
-
-	g_viewing_range_nodes = new_range;
+	draw_control.wanted_range = new_range;
 
 	range_old = new_range;
 	frametime_old = frametime;
@@ -1324,6 +1330,7 @@ int main(int argc, char *argv[])
 
 	// Initialize random seed
 	srand(time(0));
+	mysrand(time(0));
 
 	/*
 		Run unit tests
@@ -1334,12 +1341,6 @@ int main(int argc, char *argv[])
 		run_tests();
 	}
 	
-	/*
-		Global range mutex
-	*/
-	g_range_mutex.Init();
-	assert(g_range_mutex.IsInitialized());
-
 	// Read map parameters from settings
 
 	HMParams hm_params;
@@ -1584,10 +1585,7 @@ int main(int argc, char *argv[])
 		Create client
 	*/
 
-	Client client(device, playername,
-			g_range_mutex,
-			g_viewing_range_nodes,
-			g_viewing_range_all);
+	Client client(device, playername, draw_control);
 			
 	g_client = &client;
 	
@@ -2409,8 +2407,8 @@ int main(int argc, char *argv[])
 		
 		if(g_settings.getBool("enable_fog") == true)
 		{
-			f32 range = g_viewing_range_nodes * BS;
-			if(g_viewing_range_all)
+			f32 range = draw_control.wanted_range * BS;
+			if(draw_control.range_all)
 				range = 100000*BS;
 
 			driver->setFog(
@@ -2435,13 +2433,13 @@ int main(int argc, char *argv[])
 			wchar_t temptext[150];
 
 			static float drawtime_avg = 0;
-			drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
+			drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
 			static float beginscenetime_avg = 0;
-			beginscenetime_avg = beginscenetime_avg * 0.98 + (float)beginscenetime*0.02;
+			beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
 			static float scenetime_avg = 0;
-			scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
+			scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
 			static float endscenetime_avg = 0;
-			endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
+			endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
 			
 			swprintf(temptext, 150, L"Minetest-c55 ("
 					L"F: item=%i"
@@ -2449,7 +2447,7 @@ int main(int argc, char *argv[])
 					L")"
 					L" drawtime=%.0f, beginscenetime=%.0f, scenetime=%.0f, endscenetime=%.0f",
 					g_selected_item,
-					g_viewing_range_all,
+					draw_control.range_all,
 					drawtime_avg,
 					beginscenetime_avg,
 					scenetime_avg,
@@ -2472,7 +2470,7 @@ int main(int argc, char *argv[])
 					busytime_jitter1_min_sample,
 					busytime_jitter1_max_sample,
 					dtime_jitter1_max_fraction * 100.0,
-					g_viewing_range_nodes
+					draw_control.wanted_range
 					);
 
 			guitext2->setText(temptext);
diff --git a/src/map.cpp b/src/map.cpp
index d5a61d408f0c49d4101d4b774ecb96168b5bc4ee..b092e1fa538780dcfc656cd2d47246a4b6f52c83 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -2889,9 +2889,7 @@ void ServerMap::PrintInfo(std::ostream &out)
 
 ClientMap::ClientMap(
 		Client *client,
-		JMutex &range_mutex,
-		float &viewing_range_nodes,
-		bool &viewing_range_all,
+		MapDrawControl &control,
 		scene::ISceneNode* parent,
 		scene::ISceneManager* mgr,
 		s32 id
@@ -2900,9 +2898,7 @@ ClientMap::ClientMap(
 	scene::ISceneNode(parent, mgr, id),
 	m_client(client),
 	mesh(NULL),
-	m_range_mutex(range_mutex),
-	m_viewing_range_nodes(viewing_range_nodes),
-	m_viewing_range_all(viewing_range_all)
+	m_control(control)
 {
 	mesh_mutex.Init();
 
@@ -3003,24 +2999,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 	*/
 	int time1 = time(0);
 
-	//s32 daynight_i = m_client->getDayNightIndex();
 	u32 daynight_ratio = m_client->getDayNightRatio();
 
-	/*
-		Collect all blocks that are in the view range
-
-		Should not optimize more here as we want to auto-update
-		all changed nodes in viewing range at the next step.
-	*/
-
-	float viewing_range_nodes;
-	bool viewing_range_all;
-	{
-		JMutexAutoLock lock(m_range_mutex);
-		viewing_range_nodes = m_viewing_range_nodes;
-		viewing_range_all = m_viewing_range_all;
-	}
-
 	m_camera_mutex.Lock();
 	v3f camera_position = m_camera_position;
 	v3f camera_direction = m_camera_direction;
@@ -3035,7 +3015,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 			camera_position.Y / BS,
 			camera_position.Z / BS);
 
-	v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
+	v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
 
 	v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
 	v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
@@ -3054,12 +3034,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 	
 	// For limiting number of mesh updates per frame
 	u32 mesh_update_count = 0;
+	
+	u32 blocks_would_have_drawn = 0;
+	u32 blocks_drawn = 0;
 
 	//NOTE: The sectors map should be locked but we're not doing it
 	// because it'd cause too much delays
 
 	int timecheck_counter = 0;
-
 	core::map<v2s16, MapSector*>::Iterator si;
 	si = m_sectors.getIterator();
 	for(; si.atEnd() == false; si++)
@@ -3082,7 +3064,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 		MapSector *sector = si.getNode()->getValue();
 		v2s16 sp = sector->getPos();
 		
-		if(viewing_range_all == false)
+		if(m_control.range_all == false)
 		{
 			if(sp.X < p_blocks_min.X
 			|| sp.X > p_blocks_max.X
@@ -3126,12 +3108,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 			// Total distance
 			f32 d = blockpos_relative.getLength();
 			
-			if(viewing_range_all == false)
+			if(m_control.range_all == false)
 			{
 				// If block is far away, don't draw it
-				if(d > viewing_range_nodes * BS)
+				if(d > m_control.wanted_range * BS)
 				// This is nicer when fog is used
-				//if((dforward+d)/2 > viewing_range_nodes * BS)
+				//if((dforward+d)/2 > m_control.wanted_range * BS)
 					continue;
 			}
 			
@@ -3175,7 +3157,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 			}
 
 			f32 faraway = BS*50;
-			//f32 faraway = viewing_range_nodes * BS;
+			//f32 faraway = m_control.wanted_range * BS;
 			
 			/*
 				This has to be done with the mesh_mutex unlocked
@@ -3217,6 +3199,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
 				if(mesh == NULL)
 					continue;
+				
+				blocks_would_have_drawn++;
+				if(blocks_drawn >= m_control.wanted_max_blocks
+						&& m_control.range_all == false
+						&& d > m_control.wanted_min_range * BS)
+					continue;
+				blocks_drawn++;
 
 				u32 c = mesh->getMeshBufferCount();
 
@@ -3238,6 +3227,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 			}
 		} // foreach sectorblocks
 	}
+	
+	m_control.blocks_drawn = blocks_drawn;
+	m_control.blocks_would_have_drawn = blocks_would_have_drawn;
 
 	/*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
 			<<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
diff --git a/src/map.h b/src/map.h
index 8952bc1108faae56aab8a99afe9feace4da14b53..d4b790abf4739ed68aa38991cf04096d93190bca 100644
--- a/src/map.h
+++ b/src/map.h
@@ -543,6 +543,31 @@ class ServerMap : public Map
 
 #ifndef SERVER
 
+struct MapDrawControl
+{
+	MapDrawControl():
+		range_all(false),
+		wanted_range(50),
+		wanted_max_blocks(0),
+		wanted_min_range(0),
+		blocks_drawn(0),
+		blocks_would_have_drawn(0)
+	{
+	}
+	// Overrides limits by drawing everything
+	bool range_all;
+	// Wanted drawing range
+	float wanted_range;
+	// Maximum number of blocks to draw
+	u32 wanted_max_blocks;
+	// Blocks in this range are drawn regardless of number of blocks drawn
+	float wanted_min_range;
+	// Number of blocks rendered is written here by the renderer
+	u32 blocks_drawn;
+	// Number of blocks that would have been drawn in wanted_range
+	u32 blocks_would_have_drawn;
+};
+
 class Client;
 
 class ClientMap : public Map, public scene::ISceneNode
@@ -550,9 +575,7 @@ class ClientMap : public Map, public scene::ISceneNode
 public:
 	ClientMap(
 			Client *client,
-			JMutex &range_mutex,
-			float &viewing_range_nodes,
-			bool &viewing_range_all,
+			MapDrawControl &control,
 			scene::ISceneNode* parent,
 			scene::ISceneManager* mgr,
 			s32 id
@@ -618,10 +641,8 @@ class ClientMap : public Map, public scene::ISceneNode
 	// This is the master heightmap mesh
 	scene::SMesh *mesh;
 	JMutex mesh_mutex;
-
-	JMutex &m_range_mutex;
-	float &m_viewing_range_nodes;
-	bool &m_viewing_range_all;
+	
+	MapDrawControl &m_control;
 };
 
 #endif
diff --git a/src/servermain.cpp b/src/servermain.cpp
index 9a35a4b0e1f92f2bcbfc6d07f579e22462e9e295..594f7f4a236c8657bc7f71f89b363dd366d14da0 100644
--- a/src/servermain.cpp
+++ b/src/servermain.cpp
@@ -239,6 +239,7 @@ int main(int argc, char *argv[])
 
 	// Initialize random seed
 	srand(time(0));
+	mysrand(time(0));
 
 	/*
 		Run unit tests
diff --git a/src/utility.cpp b/src/utility.cpp
index 7126cbbdc57a3f2bdcaa2335d4977c1388560cbe..924324b90a8c4ff38cc9f4b6e3aad9eb397f4925 100644
--- a/src/utility.cpp
+++ b/src/utility.cpp
@@ -88,4 +88,18 @@ const v3s16 g_26dirs[26] =
 	// 26
 };
 
+static unsigned long next = 1;
+
+/* RAND_MAX assumed to be 32767 */
+int myrand(void)
+{
+   next = next * 1103515245 + 12345;
+   return((unsigned)(next/65536) % 32768);
+}
+
+void mysrand(unsigned seed)
+{
+   next = seed;
+}
+
 
diff --git a/src/utility.h b/src/utility.h
index 3764616d09373370e1873ec8d49f5552ae2a1ee5..008a95c3834c5c3f75e513056f16fa3f65b6d377 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -1323,5 +1323,10 @@ class RequestQueue
 	MutexedQueue< GetRequest<Key, T, Caller, CallerData> > m_queue;
 };
 
+// Pseudo-random (VC++ rand() sucks)
+int myrand(void);
+void mysrand(unsigned seed);
+#define MYRAND_MAX 32767
+
 #endif