From c8be58a65ce5f06bc23353b41caf9495e3b9d484 Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Mon, 28 Feb 2011 02:01:40 +0200
Subject: [PATCH] A third try on terrain generation. No trees yet.

---
 data/mud.png       | Bin 1586 -> 763 bytes
 src/client.cpp     |   4 +
 src/client.h       |   5 +
 src/clientserver.h |   1 +
 src/main.cpp       | 182 +++++++++-
 src/map.cpp        | 881 +++++++++++++++++++++++++++++++++++----------
 src/map.h          |  11 +
 src/mapblock.cpp   |   1 +
 src/noise.cpp      |  35 +-
 src/noise.h        |   6 +
 src/server.cpp     |  52 ++-
 src/tile.cpp       |   2 +-
 src/utility.h      |  20 +
 13 files changed, 974 insertions(+), 226 deletions(-)

diff --git a/data/mud.png b/data/mud.png
index 7cb9c89a637584b4e53c82a7c6eb1ec7109befae..8a72bea90aeb27e7741092499c72cae48dd4d264 100644
GIT binary patch
delta 740
zcmV<A0vr9Z4EqI;BYyw^b5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L
z01EH`01EH{Laa2H00007bV*G`2ipQ04mC0<jeCv&00MhSL_t(2&jrETa@s~10N{TQ
z((Vc!kc9wayN%s+rsL$I)9Fj}A^OC<>f|ODEhL22W55C(SATm{z7PHR$7WFO`S%a?
zcDFk<Pzv|Iep<1nwo=>m-P0_EFaPeW@)MnIImOF)`tB~M{{CEdA~Z1XNyZcJz=N3!
z7s5#eD{Krcm$fd(8OBOqR>j@1F`l#Dqa>hlCM|nNLxGEM?4aHMHAaNC-CA_JGsE=}
zb}u+!ZfK5W%70F-!B$71-IIXRhpJU|Zv{>`GALU;1`^kE(R}@fr-TERmPBrrWsBjk
z`*GuUyFSj5<LvD?WqBg%Lu))<aXdu$_+k?QO$FxKL6W8gPsjL|MH`7!Yy8u#mA!me
z@?t^p!cU-lB$6I2S82!Mw2(DS@wek>Lejf%EkqQTK!2lsHH{mN9YW>PZ^racU!Ffc
z^97u%2{Z6b)4Vk5t*WaAbYEtW3%s^#>=<=lKBbYEF?nhAW#84(nQO~t^gIx2(MYvO
znUv<*DdHxw+H8=%l*_yaNQ>;m%y?D#%x^<<%K|kCZj2qsmEI76q@_6Y3X}A|)7bg=
zxUFK_Mt@vbL|Mftz{AZM6L9##+%~`ec`{ebTon?T7a@8*#`r8$*4yE07e_EGmtvgY
z-CexCEhkCjw@*T(c<6{k;dbvDigCW+R7YUheYc4x55BG=%Zlx`QhcW6*u1=w_wQo`
zT7bM?c-ztnjS%7L4O=V3X058~aA+8^mIj8*M_p6n$BfSMABvmg@qTS4Q>idFl;NcJ
z=IyX`RA^$)2&i*|w7o^-*AJ_V>DPL|6XR4f2tGF^C$%!pQx6>GC}@Gso#C;IJo*l&
WP-s5laF@UU0000<MNUMnLSTaFrE0MN

literal 1586
zcmZuw2~d+q6kb75sRsk0q?k5{zaXLY3W9(a3WY_b(ZE<K9v~t$tWxFZXs}g_qP0L4
zTC5rBK?NNOYAQ<*7(^Tn3sP(GV5=Y_0#ZZ-u?1zj!5IgnJF~m*{qx>_-}~PC4=Mb-
zCz@KD0stmLAJ5hNHZ$yTM*K|HggXOZ3=}K;y|~K;HygYd_n2W(N(mttV}uZdP!I$P
z00964fB=912s~@VCrSY!0Aqj<fDj1(7eoOifM9?TKoCHIzceCkBs?R8VoVUi5F$Zf
z7*m7@gqYh1Q3we)Af)`{R2e0dV1f}w5J3S7gb_?JA_!tAAOSOsD1rnESU?Ox$|D5G
zpfH$E>04-!kfDGG<2RUGpVzRQivgMAj}(tFLMbMMCqjgH1f~=b0x{+`qLjbS4M-_J
z1vY{`BaC875Wx^5&KEO^D1ij?IuN6f5?&u<l-F&@9uCHp8H&dRd;`gw`x=!~;lP9N
zBa{QfmE-QvQVdOIm{5cXL|A|b2=pzj0Vg^{Zh#1VD+6M}5T+3E9$+A#;pG};1T%st
zhJ<$x0>tYaJ`E5I?++&lC<X)p9?k%v026>PA6vozrkn!cK!dARW;7ZAqYmil;Xk8x
zOex()Ljl;>&o<e|NLpI$vhPWbEi07RO^;8=J!9ixlJM=yaobGHn=a@*nx@|oX3CB_
zl?QGV&?O;krS@vgjhG<!E?2d*?|I7?6P9J+l*;Rodq1Ooc577|7E>tgTV{MB@<Hmk
zy2q|c^St{x8u5Z%vnH>b^GU|kY#+M@6QhfTqKscG$EO}~h?Y0SX~b!+=X-vW2P^CC
zOE1gUWNA;A{T|`yqmErDI$N!&=)c?jWchZ_!nMl>&VLnMmSVE1;auPn<I&q2PGznM
zxVXgkz4V8&$pHbId-Wz;zN^S@aFV4wdhYC%WUp15cXWeCy#b^s?rz<_4^|h5JF}Wp
zU7aOA9Zy6rKhn*A)to2`NI0R8Jz40!JI~#CcFntHE<OKbOT%13^z{~@);i#QCq3ep
zL>n|(q-;%E9DAfn*VT5@WtYVs_RuFy`_69T&H;7zq$%~PLRE~nZ{ai_oo0qY?o}pv
zkx_0ntu5BM^m2$(uI=?IY4OK7d;Vx^iKuUnpIhX(Q?OX->^C>e_eEm+nW#%geM*X3
zOHoAh%n6^m_38d?NK2AgJM`c5v<w+IGtX*&i&aL}@juJ=-=6yS-qPCOzYbK?IhhB=
zw;jEt31r~|V*LUOQndG^Y2~xt3$@+KUkmb6^5X7~(N}w`&ZmZ+a!Ss!X&U9P$P6kr
zI~1!qsnfl>a@ghJwKDMqkkYKL6vbCr9@%nFQnfi|&H}rRuSHf>1##()ElxLbrMJId
zpVWIcqWS5hyFCZ5_|9(3mo~82t0k>9i@%W^D{WTTtUK&jvi`KGJSuF}X1pZ4Lf6^<
zV{gGVrQ1uJ<xg)%Cf(a%7WebGkc!VIZm`@s);ZB#zv#gzO%8Eay9c*FoB#g$vKKoq
zM$L0or+V1lStw63vl34t&Y2(1Djpl4EJ~l?rghj;cT0J2{OT2DWAjAX&|5z=%8SnR
z+cxVR#gRGTTV3SRMulq1fy2p*%r{*YovdzV)?e;WJ$7*DGW-G1%g-}+>BfEk0s5W!
A1poj5

diff --git a/src/client.cpp b/src/client.cpp
index 3ea666549..fe1669ddd 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -510,6 +510,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 			assert(player != NULL);
 			player->setPosition(playerpos_f);
 		}
+
+		// Get map seed
+		m_map_seed = readU64(&data[2+1+6]);
+		dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
 		
 		// Reply to server
 		u32 replysize = 2;
diff --git a/src/client.h b/src/client.h
index fb1e70722..d6496d9df 100644
--- a/src/client.h
+++ b/src/client.h
@@ -252,6 +252,8 @@ class Client : public con::PeerHandler
 				(std::wstring)L"<"+name+L"> "+message);
 	}
 
+	u64 getMapSeed(){ return m_map_seed; }
+
 private:
 	
 	// Virtual methods from con::PeerHandler
@@ -311,6 +313,9 @@ class Client : public con::PeerHandler
 	//u32 m_daynight_ratio;
 
 	Queue<std::wstring> m_chat_queue;
+	
+	// The seed returned by the server in TOCLIENT_INIT is stored here
+	u64 m_map_seed;
 };
 
 #endif // !SERVER
diff --git a/src/clientserver.h b/src/clientserver.h
index 893bbc1e0..52b4e520e 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -34,6 +34,7 @@ enum ToClientCommand
 		[0] u16 TOSERVER_INIT
 		[2] u8 deployed version
 		[3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
+		[4] u64 map seed (new as of 2011-02-27)
 	*/
 
 	TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
diff --git a/src/main.cpp b/src/main.cpp
index 2c059840e..be3776623 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -253,11 +253,9 @@ Doing now (most important at the top):
 * not done
 
 === Next
-* Continue making the scripting system:
-  * Make updateNodeMesh for a less verbose mesh update on add/removenode
-  * Switch to using a safe way for the self and env pointers
-  * Make some global environment hooks, like node placed and general
-    on_step()
+* Make a system for pregenerating quick information for mapblocks, so
+  that the client can show them as cubes before they are actually sent
+  or even generated.
 
 === Fixmes
 * Check the fixmes in the list above
@@ -274,6 +272,11 @@ Doing now (most important at the top):
   with the ones in utility.h
 
 === Features
+* Continue making the scripting system:
+  * Make updateNodeMesh for a less verbose mesh update on add/removenode
+  * Switch to using a safe way for the self and env pointers
+  * Make some global environment hooks, like node placed and general
+    on_step()
 * Map should make the appropriate MapEditEvents
 * Add a global Lua spawn handler and such
 * Get rid of MapBlockObjects
@@ -490,6 +493,9 @@ Inventory local_inventory;
 
 u16 g_selected_item = 0;
 
+bool g_show_map_plot = false;
+bool g_refresh_map_plot = true;
+
 /*
 	Debug streams
 */
@@ -606,6 +612,15 @@ class MyEventReceiver : public IEventReceiver
 			if(event.KeyInput.PressedDown)
 			{
 				//dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
+				if(g_show_map_plot)
+				{
+					if(event.KeyInput.Key == irr::KEY_ESCAPE
+						|| event.KeyInput.Key == irr::KEY_KEY_M)
+					{
+						g_show_map_plot = false;
+					}
+					return true;
+				}
 				
 				/*
 					Launch menus
@@ -679,6 +694,16 @@ class MyEventReceiver : public IEventReceiver
 							<<std::endl;
 					debug_stacks_print();
 				}
+
+				// Map plot
+				if(event.KeyInput.Key == irr::KEY_KEY_M)
+				{
+					dstream<<"Map plot requested"<<std::endl;
+					g_show_map_plot = !g_show_map_plot;
+					if(g_show_map_plot)
+						g_refresh_map_plot = true;
+				}
+				
 			}
 		}
 
@@ -1110,8 +1135,8 @@ void updateViewingRange(f32 frametime_in, Client *client)
 	f32 wanted_frametime_change = wanted_frametime - frametime;
 	//dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
 	
-	// If needed frametime change is very small, just return
-	if(fabs(wanted_frametime_change) < wanted_frametime*0.2)
+	// If needed frametime change is small, just return
+	if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
 	{
 		//dstream<<"ignoring small wanted_frametime_change"<<std::endl;
 		return;
@@ -1239,6 +1264,93 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
 	}
 }
 
+video::ITexture *g_map_plot_texture = NULL;
+float g_map_plot_texture_scale = 2;
+
+void updateMapPlotTexture(v2f centerpos, video::IVideoDriver* driver,
+		Client *client)
+{
+	assert(driver);
+	assert(client);
+
+	core::dimension2d<u32> dim(640,480);
+	video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
+	assert(img);
+	for(u32 y=0; y<dim.Height; y++)
+	for(u32 x=0; x<dim.Width; x++)
+	{
+		v2f pf = v2f(x, dim.Height-y) - v2f(dim.Width, dim.Height)/2;
+		pf *= g_map_plot_texture_scale;
+		pf += centerpos;
+		double h = base_rock_level_2d(client->getMapSeed(), pf);
+		video::SColor c;
+		//double d1 = 50;
+		/*s32 ux = x - centerpos.X / g_map_plot_texture_scale;
+		s32 uy = y - centerpos.Y / g_map_plot_texture_scale;*/
+			
+		// Screen coordinates that are based on multiples of
+		// 1000/g_map_plot_texture_scale and never negative
+		u32 ux = x + (u32)(1000/g_map_plot_texture_scale) * 10;
+		u32 uy = y + (u32)(1000/g_map_plot_texture_scale) * 10;
+		// Offset to center of image
+		ux -= dim.Width/2;
+		uy -= dim.Height/2;
+
+		if(uy % (u32)(1000/g_map_plot_texture_scale) == 0
+				|| ux % (u32)(1000/g_map_plot_texture_scale) == 0)
+			c.set(255, 255, 255, 255);
+		else if(uy % (u32)(100/g_map_plot_texture_scale) == 0
+				|| ux % (u32)(100/g_map_plot_texture_scale) == 0)
+			c.set(255, 160, 160, 160);
+		else if(h < WATER_LEVEL - 0.5) // Water
+			c.set(255, 50, 50, 255);
+		else if(h < WATER_LEVEL + 2) // Sand
+			c.set(255, 237, 201, 175);
+#if 1
+		else if(h < WATER_LEVEL + 10) // Green
+			c.set(255, 50, 150, 50);
+		else if(h < WATER_LEVEL + 20) // Greenish yellow
+			c.set(255, 110, 185, 50);
+		else if(h < WATER_LEVEL + 50) // Yellow
+			c.set(255, 220, 220, 50);
+		else if(h < WATER_LEVEL + 100) // White
+			c.set(255, 180, 180, 180);
+		else
+			c.set(255, 255, 255, 255);
+#endif
+		/*else if(h < WATER_LEVEL + d1)
+		{
+			h -= WATER_LEVEL;
+			u32 a = (u32)(h / d1 * 255);
+			if(a > 255)
+				a = 255;
+			c.set(255, 0, a, 0);
+		}*/
+#if 0
+		else
+		{
+			h -= WATER_LEVEL;
+			h /= 20.0;
+			h = 1.0 - exp(-h);
+
+			video::SColor c1(255,200,200,50);
+			video::SColor c2(255,0,150,0);
+			c = c1.getInterpolated(c2, h);
+
+			/*u32 a = (u32)(h*255);
+			if(a > 255)
+				a = 255;
+			a = 255-a;
+			c.set(255, a, a, a);*/
+		}
+#endif
+		img->setPixel(x, y, c);
+	}
+	g_map_plot_texture = driver->addTexture("map_plot", img);
+	img->drop();
+	assert(g_map_plot_texture);
+}
+
 // Chat data
 struct ChatLine
 {
@@ -2252,6 +2364,10 @@ int main(int argc, char *argv[])
 		*/
 		g_input->step(dtime);
 
+		/*
+			Misc. stuff
+		*/
+
 		/*
 			Player speed control
 		*/
@@ -3020,16 +3136,6 @@ int main(int argc, char *argv[])
 			driver->draw3DBox(*i, video::SColor(255,0,0,0));
 		}
 
-		/*
-			Draw crosshair
-		*/
-		driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
-				displaycenter + core::vector2d<s32>(10,0),
-				video::SColor(255,255,255,255));
-		driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
-				displaycenter + core::vector2d<s32>(0,10),
-				video::SColor(255,255,255,255));
-
 		/*
 			Frametime log
 		*/
@@ -3048,6 +3154,31 @@ int main(int argc, char *argv[])
 			}
 		}
 
+		/*
+			Draw map plot
+		*/
+		if(g_show_map_plot && g_map_plot_texture)
+		{
+			core::dimension2d<u32> drawdim(640,480);
+			core::rect<s32> dest(v2s32(0,0), drawdim);
+			dest += v2s32(
+				(screensize.X-drawdim.Width)/2,
+				(screensize.Y-drawdim.Height)/2
+			);
+			core::rect<s32> source(v2s32(0,0), g_map_plot_texture->getSize());
+			driver->draw2DImage(g_map_plot_texture, dest, source);
+		}
+
+		/*
+			Draw crosshair
+		*/
+		driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
+				displaycenter + core::vector2d<s32>(10,0),
+				video::SColor(255,255,255,255));
+		driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
+				displaycenter + core::vector2d<s32>(0,10),
+				video::SColor(255,255,255,255));
+
 		} // timer
 
 		//timer10.stop();
@@ -3077,8 +3208,23 @@ int main(int argc, char *argv[])
 		drawtime = drawtimer.stop(true);
 
 		/*
-			Drawing ends
+			End of drawing
+		*/
+
+		/*
+			Refresh map plot if player has moved considerably
 		*/
+		if(g_refresh_map_plot)
+		{
+			static v3f old_player_pos = v3f(1,1,1) * 10000000;
+			v3f p = client.getPlayerPosition() / BS;
+			if(old_player_pos.getDistanceFrom(p) > 4 * g_map_plot_texture_scale)
+			{
+				updateMapPlotTexture(v2f(p.X,p.Z), driver, &client);
+				old_player_pos = p;
+			}
+			g_refresh_map_plot = false;
+		}
 		
 		static s16 lastFPS = 0;
 		//u16 fps = driver->getFPS();
diff --git a/src/map.cpp b/src/map.cpp
index 8e71212b0..7b16834e2 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1422,6 +1422,9 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 
 	while(m_transforming_liquid.size() != 0)
 	{
+		try
+		{
+
 		/*
 			Get a queued transforming liquid node
 		*/
@@ -1680,9 +1683,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 		}
 
 		loopcount++;
-		//if(loopcount >= 100000)
-		if(loopcount >= initial_size * 1)
+		if(loopcount >= initial_size * 1 || loopcount >= 1000)
 			break;
+			
+		}catch(InvalidPositionException &e)
+		{
+		}
 	}
 	//dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
 }
@@ -1698,8 +1704,8 @@ ServerMap::ServerMap(std::string savedir):
 	
 	//m_chunksize = 64;
 	//m_chunksize = 16; // Too slow
-	m_chunksize = 8; // Takes a few seconds
-	//m_chunksize = 4;
+	//m_chunksize = 8; // Takes a few seconds
+	m_chunksize = 4; // Too small?
 	//m_chunksize = 2;
 	
 	// TODO: Save to and load from a file
@@ -1954,61 +1960,288 @@ double tree_amount_2d(u64 seed, v2s16 p)
 
 #define AVERAGE_MUD_AMOUNT 4
 
-double base_rock_level_2d(u64 seed, v2s16 p)
+double get_mud_amount(u64 seed, v2f p)
+{
+	return ((float)AVERAGE_MUD_AMOUNT + 2.5 * noise2d_perlin(
+			0.5+p.X/200, 0.5+p.Y/200,
+			seed+1, 5, 0.65));
+}
+
+// -1->0, 0->1, 1->0
+double contour(double v)
 {
-	// The base ground level
-	double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
-			+ 25. * noise2d_perlin(
+	v = fabs(v);
+	if(v >= 1.0)
+		return 0.0;
+	return (1.0-v);
+}
+
+// -1->0, -r->1, 0->1, r->1, 1->0
+double contour_flat_top(double v, double r)
+{
+	v = fabs(v);
+	if(v >= 1.0)
+		return 0.0;
+	double rmax = 0.999;
+	if(r >= rmax)
+		r = rmax;
+	if(v <= r)
+		return 1.0;
+	v -= r;
+	return ((1.0-r)-v) / (1.0-r);
+	//return easeCurve(((1.0-r)-v) / (1.0-r));
+}
+
+double base_rock_level_2d(u64 seed, v2f p)
+{
+	// The ground level (return value)
+	double h = WATER_LEVEL;
+	
+	// Raises from 0 when parameter is -1...1
+	/*double m2 = contour_flat_top(-0.8 + 2.0 * noise2d_perlin(
+			0.0+(float)p.X/1500., 0.0+(float)p.Y/1500.,
+			(seed>>32)+34758, 5, 0.55), 0.10);*/
+	/*double m2 = 1.0;
+	if(m2 > 0.0001)
+	{
+		// HUGE mountains
+		double m1 = 200.0 + 300.0 * noise2d_perlin(
+				0.0+(float)p.X/1000., 0.0+(float)p.Y/1000.,
+				(seed>>32)+98525, 8, 0.5);
+		h += m1 * m2;
+		//h += 30 * m2;
+	}*/
+
+	// Huge mountains
+	double m3 = 150.0 - 800.0 * noise2d_perlin_abs(
+			0.5+(float)p.X/2000., 0.5+(float)p.Y/2000.,
+			(seed>>32)+985251, 9, 0.5);
+	if(m3 > h)
+		h = m3;
+
+	/*double tm2 = contour_flat_top(-1.0 + 3.0 * noise2d_perlin(
+			0.0+(float)p.X/300., 0.0+(float)p.Y/300.,
+			(seed>>32)+78593, 5, 0.55), 0.15);
+	h += 30 * tm2;*/
+
+#if 1
+	{
+		double a1 = 30 - 100. * noise2d_perlin_abs(
+				0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+				seed+850342, 5, 0.63);
+		double d = 15;
+		if(a1 > d)
+			a1 = d + sqrt(a1-d);
+		if(a1 > h)
+			h = a1;
+	}
+#endif
+
+#if 1
+	double base = 35. * noise2d_perlin(
 			0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-			(seed>>32)+654879876, 6, 0.6);
+			(seed>>32)+653876, 7, 0.55);
+#else
+	double base = 0;
+#endif
 	
-	/*// A bit hillier one
-	double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			(seed>>27)+90340, 6, 0.69);
-	if(base2 > base)
-		base = base2;*/
 #if 1
-	// Higher ground level
-	double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
+	double higher = 50. * noise2d_perlin(
 			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed+85039, 5, 0.69);
-	//higher = 30; // For debugging
-
-	// Limit higher to at least base
-	if(higher < base)
-		higher = base;
-		
-	// Steepness factor of cliffs
-	double b = 1.0 + 1.0 * noise2d_perlin(
+			seed+39292, 6, 0.63);
+	/*double higher = 50. * noise2d_perlin_abs(
 			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed-932, 7, 0.7);
-	b = rangelim(b, 0.0, 1000.0);
-	b = pow(b, 5);
-	b *= 7;
-	b = rangelim(b, 3.0, 1000.0);
-	//dstream<<"b="<<b<<std::endl;
-	//double b = 20;
-
-	// Offset to more low
-	double a_off = -0.2;
-	// High/low selector
-	/*double a = 0.5 + b * (a_off + noise2d_perlin(
-			0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-			seed-359, 6, 0.7));*/
-	double a = (double)0.5 + b * (a_off + noise2d_perlin(
-			0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-			seed-359, 5, 0.60));
-	// Limit
-	a = rangelim(a, 0.0, 1.0);
+			seed+85039, 5, 0.63);*/
+
+	if(higher > base)
+	{
+		// Steepness factor of cliffs
+		double b = 1.0 + 1.0 * noise2d_perlin(
+				0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+				seed-932, 7, 0.7);
+		b = rangelim(b, 0.0, 1000.0);
+		b = pow(b, 5);
+		b *= 7;
+		b = rangelim(b, 3.0, 1000.0);
+		//dstream<<"b="<<b<<std::endl;
+		//double b = 20;
+
+		// Offset to more low
+		//double a_off = -0.30;
+		double a_off = -0.00;
+		// High/low selector
+		double a = (double)0.5 + b * (a_off + noise2d_perlin(
+				0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+				seed-359, 5, 0.6));
+		// Limit
+		a = rangelim(a, 0.0, 1.0);
+		//a = easeCurve(a);
+
+		//dstream<<"a="<<a<<std::endl;
+		
+		h += base*(1.0-a) + higher*a;
+	}
+	else
+	{
+		h += base;
+	}
+#else
+	h += base;
+#endif
 
-	//dstream<<"a="<<a<<std::endl;
+	return h;
+}
+
+double base_rock_level_2d(u64 seed, v2s16 p)
+{
+	return base_rock_level_2d(seed, v2f((float)p.X, (float)p.Y));
+}
+
+v2f base_ground_turbulence(u64 seed, v3f p)
+{
+	double f = 12;
+
+	double v1 = f * noise3d_perlin(
+			0.5+p.X/200,
+			0.5+p.Y/200,
+			0.5+p.Z/200,
+			seed+4045, 5, 0.7);
+
+	double v2 = f * noise3d_perlin(
+			0.5+p.X/200,
+			0.5+p.Y/200,
+			0.5+p.Z/200,
+			seed+9495, 5, 0.7);
 	
-	double h = base*(1.0-a) + higher*a;
+	return v2f(v1, v2);
+}
+
+bool is_carved(u64 seed, v3f p)
+{
+#if 1
+	double v1 = noise3d_perlin_abs(
+			0.5+p.X/200,
+			0.5+p.Y/200,
+			0.5+p.Z/200,
+			seed+657890854, 5, 0.7);
+	
+	if(v1 > 1.5)
+		return true;
+#endif
+
+#if 0
+	double v2 = noise3d_perlin_abs(
+			0.5+p.X/200,
+			0.5+p.Y/200,
+			0.5+p.Z/200,
+			seed+657890854, 5, 0.7);
+#if 0
+	double v3 = noise3d_perlin_abs(
+			0.5+p.X/200,
+			0.5+p.Y/200,
+			0.5+p.Z/200,
+			seed+657890854, 5, 0.7);
 #else
-	double h = base;
+	double v3 = 1.0;
 #endif
-	return h;
+	double v23 = v2*v3;
+	if(v23 > 0.7)
+		return true;
+#endif
+	
+	double f = 10.0;
+	double y_div = 1.5;
+
+	double v4 = contour(f*noise3d_perlin(
+			0.5+p.X/200,
+			0.5+p.Y/200*y_div,
+			0.5+p.Z/200,
+			seed+87592, 5, 0.7));
+	// Tilted 90 degrees
+	double v5 = contour(f*noise3d_perlin(
+			0.5+p.X/200,
+			0.5+p.Z/200,
+			0.5+p.Y/200*y_div,
+			seed+98594, 5, 0.7));
+	
+	double v45 = v4*v5;
+	if(v45 > 2.5/f)
+		return true;
+	
+	return false;
+}
+
+/*
+	if depth_guess!=NULL, it is set to a guessed value of how deep
+	underground the position is.
+*/
+bool is_base_ground(u64 seed, v3f p, double *depth_guess=NULL)
+{
+#if 0
+	// This is used for testing the output of the cave function
+	{
+		if(depth_guess)
+			*depth_guess = 10;
+		if(p.Y > 50)
+			return false;
+		return is_carved(seed, p);
+	}
+#endif
+
+	v2f t = base_ground_turbulence(seed, p);
+
+	double surface_y_f = base_rock_level_2d(seed, v2f(p.X+t.X, p.Z+t.Y));
+
+	/*if(depth_guess)
+		*depth_guess = surface_y_f - p.Y;*/
+	
+	if(depth_guess)
+	{
+		// Find highest surface near current
+		v3f dirs[4] = {
+			v3f(1,-1,0),
+			v3f(-1,-1,0),
+			v3f(0,-1,1),
+			v3f(0,-1,-1)
+		};
+		double s2 = surface_y_f;
+		for(u32 i=0; i<4; i++)
+		{
+			v3f dir = dirs[i];
+			v2f l = v2f(p.X+t.X+dir.X, p.Z+t.Y+dir.Z);
+			double s = base_rock_level_2d(seed, l);
+			if(s > s2)
+				s2 = s;
+		}
+		*depth_guess = s2 - p.Y;
+	}
+	
+	/*if(depth_guess)
+	{
+		// Check a bit lower also, take highest surface
+		v2f t2 = base_ground_turbulence(seed, p + v3f(0,-2,0));
+		double s2 = base_rock_level_2d(seed, v2f(p.X+t2.X, p.Z+t2.Y));
+		if(s2 > surface_y_f)
+			*depth_guess = s2 - p.Y;
+		else
+			*depth_guess = surface_y_f - p.Y;
+	}*/
+	
+	/*if(depth_guess)
+	{
+		// Guess surface point
+		v3f p2(p.X, surface_y_f, p.Z);
+		v2f t2 = base_ground_turbulence
+		double u1 = 
+		double s1 = base_rock_level_2d(seed, v2f(p.X+v1,p.Z+v2));
+	}*/
+
+	bool is_ground = (p.Y <= surface_y_f);
+	
+	if(is_carved(seed, p))
+		is_ground = false;
+
+	return is_ground;
 }
 
 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
@@ -2023,6 +2256,11 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 {
 	DSTACK(__FUNCTION_NAME);
 
+	// Shall be not used now
+	//assert(0);
+
+#if 0
+
 	/*
 		Don't generate if already fully generated
 	*/
@@ -2163,7 +2401,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 	
 	{
 	// 22ms @cs=8
-	//TimeTaker timer1("ground level");
+	TimeTaker timer1("ground level");
+	dstream<<"Generating base ground..."<<std::endl;
 
 	for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
 	for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
@@ -2172,7 +2411,79 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 		v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
 		
 		/*
-			Skip of already generated
+			Skip if already generated
+		*/
+		{
+			v3s16 p(p2d.X, y_nodes_min, p2d.Y);
+			if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
+				continue;
+		}
+
+		v2f p2df(p2d.X, p2d.Y);
+
+		{
+			// Use fast index incrementing
+			v3s16 em = vmanip.m_area.getExtent();
+			s16 min = y_nodes_min;
+			s16 max = y_nodes_max;
+			/*s16 min = -10;
+			s16 max = 20;*/
+			//float surface_y_f = base_rock_level_2d(m_seed, p2df);
+			u32 i = vmanip.m_area.index(v3s16(p2d.X, min, p2d.Y));
+			for(s16 y=min; y<=max; y++)
+			{
+#if 1
+				bool is = is_base_ground(m_seed, v3f(p2df.X,y,p2df.Y));
+				if(is)
+					vmanip.m_data[i].d = CONTENT_STONE;
+				else
+					vmanip.m_data[i].d = CONTENT_AIR;
+#endif
+#if 0
+				double v = noise3d_perlin(
+						0.5+(float)p2d.X/200,
+						0.5+(float)y/200,
+						0.5+(float)p2d.Y/200,
+						m_seed+293, 6, 0.55);
+				if(v > 0.0)
+					vmanip.m_data[i].d = CONTENT_STONE;
+				else
+					vmanip.m_data[i].d = CONTENT_AIR;
+#endif
+#if 0
+				/*double v1 = 5 * noise3d_perlin(
+						0.5+(float)p2df.X/200,
+						0.5+(float)y/200,
+						0.5+(float)p2df.Y/200,
+						m_seed+293, 6, 0.55);
+
+				double v2 = 5 * noise3d_perlin(
+						0.5+(float)p2df.X/200,
+						0.5+(float)y/200,
+						0.5+(float)p2df.Y/200,
+						m_seed+293, 6, 0.55);*/
+
+				double v1 = 0;
+				double v2 = 0;
+
+				float surface_y_f = base_rock_level_2d(m_seed, p2df+v2f(v1,v2));
+
+				if(y <= surface_y_f)
+					vmanip.m_data[i].d = CONTENT_STONE;
+				else
+					vmanip.m_data[i].d = CONTENT_AIR;
+#endif
+
+				vmanip.m_area.add_y(em, i, 1);
+			}
+		}
+
+#if 0
+		// Node position
+		v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
+
+		/*
+			Skip if already generated
 		*/
 		{
 			v3s16 p(p2d.X, y_nodes_min, p2d.Y);
@@ -2214,6 +2525,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 				vmanip.m_area.add_y(em, i, 1);
 			}
 		}
+#endif
 	}
 	
 	}//timer1
@@ -2235,8 +2547,8 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 	/*
 		Loop this part, it will make stuff look older and newer nicely
 	*/
-	//for(u32 i_age=0; i_age<1; i_age++)
-	for(u32 i_age=0; i_age<2; i_age++)
+	u32 age_count = 2;
+	for(u32 i_age=0; i_age<age_count; i_age++)
 	{ // Aging loop
 
 	{
@@ -2732,9 +3044,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
 		
 		// Randomize mud amount
-		s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
-				0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
-				m_seed+1, 3, 0.55));
+		s16 mud_add_amount = get_mud_amount(m_seed, v2f(p2d.X,p2d.Y))/age_count;
 
 		// Find ground level
 		s16 surface_y = find_ground_level_clever(vmanip, p2d);
@@ -2777,7 +3087,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 	}//timer1
 	{
 	// 340ms @cs=8
-	TimeTaker timer1("flow mud");
+	//TimeTaker timer1("flow mud");
 
 	/*
 		Flow mud away from steep edges
@@ -3447,57 +3757,6 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 	}
 #endif
 
-#if 0
-	for(s16 x=lighting_min_d+1;
-			x<=lighting_max_d-1;
-			x++)
-	for(s16 z=lighting_min_d+1;
-			z<=lighting_max_d-1;
-			z++)
-	{
-		// Node position in 2d
-		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
-		
-		/*
-			Apply initial sunlight
-		*/
-		{
-			u8 light = LIGHT_SUN;
-			v3s16 em = vmanip.m_area.getExtent();
-			s16 y_start = y_nodes_max;
-			u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
-			for(s16 y=y_start; y>=y_nodes_min; y--)
-			{
-				MapNode *n = &vmanip.m_data[i];
-
-				if(light_propagates_content(n->d) == false)
-				{
-					light = 0;
-				}
-				else if(light != LIGHT_SUN
-					|| sunlight_propagates_content(n->d) == false)
-				{
-					if(light > 0)
-						light--;
-				}
-				
-				n->setLight(LIGHTBANK_DAY, light);
-				n->setLight(LIGHTBANK_NIGHT, 0);
-				
-				// This doesn't take much time
-				if(light != 0)
-				{
-					// Insert light source
-					light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
-				}
-				
-				// Increment index by y
-				vmanip.m_area.add_y(em, i, -1);
-			}
-		}
-	}
-#endif
-
 	}//timer1
 
 	// Spread light around
@@ -3533,6 +3792,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
 		}
 	}
 
+#endif
 	
 	/*
 		Create chunk metadata
@@ -3581,6 +3841,9 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
 			<<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
 			<<std::endl;
 	
+	// Shall be not used now
+	//assert(0);
+	
 	/*for(s16 x=-1; x<=1; x++)
 	for(s16 y=-1; y<=1; y++)*/
 	for(s16 x=-0; x<=0; x++)
@@ -3709,13 +3972,6 @@ MapSector * ServerMap::emergeSector(v2s16 p2d,
 			<<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
 			<<std::endl;
 
-#if 0
-	dstream<<"WARNING: Creating an empty sector."<<std::endl;
-
-	return createSector(p2d);
-	
-#endif
-	
 #if 1
 	dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
 
@@ -3731,7 +3987,14 @@ MapSector * ServerMap::emergeSector(v2s16 p2d,
 	
 	dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
 	
-	assert(0);
+	//assert(0);
+#endif
+	
+#if 1
+	dstream<<"WARNING: Creating an empty sector."<<std::endl;
+
+	return createSector(p2d);
+	
 #endif
 	
 	/*
@@ -3765,6 +4028,7 @@ MapBlock * ServerMap::generateBlock(
 	v2s16 p2d(p.X, p.Z);
 	s16 block_y = p.Y;
 	v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
+	v3s16 p_nodes = p * MAP_BLOCKSIZE;
 	
 	/*
 		Do not generate over-limit
@@ -3797,99 +4061,324 @@ MapBlock * ServerMap::generateBlock(
 	
 	s32 lowest_ground_y = 32767;
 	s32 highest_ground_y = -32768;
-	
-	for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-	for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+
+	enum{
+		BT_GROUND,
+		BT_SURFACE,
+		BT_SKY
+	} block_type = BT_SURFACE;
+
+	{// ground_timer (0ms or ~100ms)
+	//TimeTaker ground_timer("Ground generation");
+
+	/*
+		Approximate whether this block is a surface block, an air
+		block or a ground block.
+
+		This shall never mark a surface block as non-surface.
+	*/
+
 	{
-		//dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
+		/*
+			Estimate surface at different positions of the block, to
+			try to accomodate the effect of turbulence.
+		*/
+		v3f checklist[] = {
+			v3f(0,0,0),
+			v3f(0,1,0),
+			v3f(0,1,1),
+			v3f(0,0,1),
+			v3f(1,0,0),
+			v3f(1,1,0),
+			v3f(1,1,1),
+			v3f(1,0,1),
+			v3f(0.5,0.5,0.5),
+		};
+		v3f p_nodes_f = intToFloat(p_nodes, 1);
+		float surface_y_max = -1000000;
+		float surface_y_min = 1000000;
+		for(u32 i=0; i<sizeof(checklist)/sizeof(checklist[0]); i++)
+		{
+			v3f p_map_f = p_nodes_f + checklist[i]*MAP_BLOCKSIZE;
 
-		//s16 surface_y = 0;
+			double depth_guess;
+			bool is_ground = is_base_ground(m_seed, p_map_f, &depth_guess);
+			
+			// Estimate the surface height
+			float surface_y_f = p_map_f.Y + depth_guess;
 
-		s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
-				+ AVERAGE_MUD_AMOUNT;
+			if(surface_y_f > surface_y_max)
+				surface_y_max = surface_y_f;
+			if(surface_y_f < surface_y_min)
+				surface_y_min = surface_y_f;
+		}
 
-		if(surface_y < lowest_ground_y)
-			lowest_ground_y = surface_y;
-		if(surface_y > highest_ground_y)
-			highest_ground_y = surface_y;
+		float block_low_y_f = p_nodes_f.Y;
+		float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE;
 
-		s32 surface_depth = AVERAGE_MUD_AMOUNT;
+		/*dstream<<"surface_y_max="<<surface_y_max
+				<<", surface_y_min="<<surface_y_min
+				<<", block_low_y_f="<<block_low_y_f
+				<<", block_high_y_f="<<block_high_y_f
+				<<std::endl;*/
 		
-		for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+		// A fuzzyness value
+		// Must accomodate mud and turbulence holes
+		float d_down = 16;
+		// Must accomodate a bit less
+		float d_up = 5;
+
+		if(block_high_y_f < surface_y_min - d_down)
 		{
-			s16 real_y = block_y * MAP_BLOCKSIZE + y0;
-			MapNode n;
-			/*
-				Calculate lighting
-				
-				NOTE: If there are some man-made structures above the
-				newly created block, they won't be taken into account.
-			*/
-			if(real_y > surface_y)
-				n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+			//dstream<<"BT_GROUND"<<std::endl;
+			// A ground block
+			//block_type = BT_GROUND;
+			// Handled as surface because of caves
+			block_type = BT_SURFACE;
+		}
+		else if(block_low_y_f >= surface_y_max + d_up
+				&& block_low_y_f > WATER_LEVEL + d_up)
+		{
+			//dstream<<"BT_SKY"<<std::endl;
+			// A sky block
+			block_type = BT_SKY;
+		}
+		else
+		{
+			//dstream<<"BT_SURFACE"<<std::endl;
+			// A surface block
+			block_type = BT_SURFACE;
+		}
 
-			/*
-				Calculate material
-			*/
+		if(block_type == BT_GROUND || block_type == BT_SKY)
+		{
+			lowest_ground_y = surface_y_min;
+			highest_ground_y = surface_y_max;
+		}
+	}
+	
+	if(block_type == BT_SURFACE)
+	{
+		/*
+			Generate ground precisely
+		*/
+		
+		for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+		for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+		{
+			//dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
 
-			// If node is over heightmap y, it's air or water
-			if(real_y > surface_y)
-			{
-				// If under water level, it's water
-				if(real_y < WATER_LEVEL)
-				{
-					n.d = water_material;
-					n.setLight(LIGHTBANK_DAY,
-							diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
-					/*
-						Add to transforming liquid queue (in case it'd
-						start flowing)
-					*/
-					v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
-					m_transforming_liquid.push_back(real_pos);
-				}
-				// else air
-				else
-					n.d = CONTENT_AIR;
-			}
-			// Else it's ground or dungeons (air)
-			else
+			//s16 surface_y = 0;
+
+			/*s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
+					+ AVERAGE_MUD_AMOUNT;
+
+			if(surface_y < lowest_ground_y)
+				lowest_ground_y = surface_y;
+			if(surface_y > highest_ground_y)
+				highest_ground_y = surface_y;*/
+
+			v2s16 real_p2d = v2s16(x0,z0) + p2d*MAP_BLOCKSIZE;
+
+			//s32 surface_depth = AVERAGE_MUD_AMOUNT;
+			s16 surface_depth = get_mud_amount(m_seed, v2f(real_p2d.X,real_p2d.Y));
+			
+			for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
 			{
-				// If it's surface_depth under ground, it's stone
-				if(real_y <= surface_y - surface_depth)
+	#if 1
+				s16 real_y = block_y * MAP_BLOCKSIZE + y0;
+				v3s16 real_pos = v3s16(x0,y0,z0) + p_nodes;
+				MapNode n;
+				/*
+					Calculate lighting
+					
+					NOTE: If there are some man-made structures above the
+					newly created block, they won't be taken into account.
+				*/
+				/*if(real_y > surface_y)
+					n.setLight(LIGHTBANK_DAY, LIGHT_SUN);*/
+
+				/*
+					Calculate material
+				*/
+				
+				double depth_guess;
+				bool is_ground = is_base_ground(m_seed,
+						intToFloat(real_pos, 1), &depth_guess);
+				
+				// Estimate the surface height
+				float surface_y_f = (float)real_y + depth_guess;
+				s16 surface_y = real_y + depth_guess;
+				
+				// Get some statistics of surface height
+				if(surface_y < lowest_ground_y)
+					lowest_ground_y = surface_y;
+				if(surface_y > highest_ground_y)
+					highest_ground_y = surface_y;
+
+				// If node is not ground, it's air or water
+				if(is_ground == false)
 				{
-					n.d = CONTENT_STONE;
+					// If under water level, it's water
+					if(real_y < WATER_LEVEL)
+					{
+						n.d = water_material;
+						n.setLight(LIGHTBANK_DAY,
+								diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
+						/*
+							Add to transforming liquid queue (in case it'd
+							start flowing)
+						*/
+						m_transforming_liquid.push_back(real_pos);
+					}
+					// else air
+					else
+						n.d = CONTENT_AIR;
 				}
+				// Else it's ground or dungeons (air)
 				else
 				{
-					// It is mud if it is under the first ground
-					// level or under water
-					if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
+					// If it's surface_depth under ground, it's stone
+					if((float)real_y <= surface_y_f - surface_depth - 0.75)
 					{
-						n.d = CONTENT_MUD;
+						n.d = CONTENT_STONE;
+					}
+					else if(surface_y_f <= WATER_LEVEL + 2.0)
+					{
+						n.d = CONTENT_SAND;
 					}
 					else
 					{
-						n.d = CONTENT_GRASS;
+						/*// It is mud if it is under the first ground
+						// level or under water
+						if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
+						{
+							n.d = CONTENT_MUD;
+						}
+						else
+						{
+							n.d = CONTENT_GRASS;
+						}*/
+
+						n.d = CONTENT_MUD;
+						
+						/*// If under water level, it's mud
+						if(real_y < WATER_LEVEL)
+							n.d = CONTENT_MUD;
+						// Only the topmost node is grass
+						else if(real_y <= surface_y - 1)
+							n.d = CONTENT_MUD;
+						else
+							n.d = CONTENT_GRASS;*/
 					}
+				}
 
-					//n.d = CONTENT_MUD;
+				block->setNode(v3s16(x0,y0,z0), n);
+	#endif
+	#if 0
+				s16 real_y = block_y * MAP_BLOCKSIZE + y0;
+				MapNode n;
+				/*
+					Calculate lighting
 					
-					/*// If under water level, it's mud
+					NOTE: If there are some man-made structures above the
+					newly created block, they won't be taken into account.
+				*/
+				if(real_y > surface_y)
+					n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+
+				/*
+					Calculate material
+				*/
+
+				// If node is over heightmap y, it's air or water
+				if(real_y > surface_y)
+				{
+					// If under water level, it's water
 					if(real_y < WATER_LEVEL)
-						n.d = CONTENT_MUD;
-					// Only the topmost node is grass
-					else if(real_y <= surface_y - 1)
-						n.d = CONTENT_MUD;
+					{
+						n.d = water_material;
+						n.setLight(LIGHTBANK_DAY,
+								diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
+						/*
+							Add to transforming liquid queue (in case it'd
+							start flowing)
+						*/
+						v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
+						m_transforming_liquid.push_back(real_pos);
+					}
+					// else air
 					else
-						n.d = CONTENT_GRASS;*/
+						n.d = CONTENT_AIR;
 				}
+				// Else it's ground or dungeons (air)
+				else
+				{
+					// If it's surface_depth under ground, it's stone
+					if(real_y <= surface_y - surface_depth)
+					{
+						n.d = CONTENT_STONE;
+					}
+					else
+					{
+						// It is mud if it is under the first ground
+						// level or under water
+						if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
+						{
+							n.d = CONTENT_MUD;
+						}
+						else
+						{
+							n.d = CONTENT_GRASS;
+						}
+
+						//n.d = CONTENT_MUD;
+						
+						/*// If under water level, it's mud
+						if(real_y < WATER_LEVEL)
+							n.d = CONTENT_MUD;
+						// Only the topmost node is grass
+						else if(real_y <= surface_y - 1)
+							n.d = CONTENT_MUD;
+						else
+							n.d = CONTENT_GRASS;*/
+					}
+				}
+
+				block->setNode(v3s16(x0,y0,z0), n);
+	#endif
 			}
+		}
+	}// BT_SURFACE
+	else // BT_GROUND, BT_SKY or anything else
+	{
+		MapNode n_fill;
+		if(block_type == BT_GROUND)
+		{
+			n_fill.d = CONTENT_STONE;
+		}
+		else if(block_type == BT_SKY)
+		{
+			n_fill.d = CONTENT_AIR;
+			n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+		}
+		else // fallback
+		{
+			n_fill.d = CONTENT_MESE;
+		}
 
-			block->setNode(v3s16(x0,y0,z0), n);
+
+		for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+		for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+		for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+		{
+			//MapNode n = block->getNode(v3s16(x0,y0,z0));
+			block->setNode(v3s16(x0,y0,z0), n_fill);
 		}
 	}
 	
+	}// ground_timer
+	
 	/*
 		Calculate some helper variables
 	*/
@@ -3945,7 +4434,7 @@ MapBlock * ServerMap::generateBlock(
 	}
 	
 	// Fill table
-#if 1
+#if 0
 	{
 		/*
 			Initialize orp and ors. Try to find if some neighboring
@@ -4087,7 +4576,8 @@ MapBlock * ServerMap::generateBlock(
 		// Partly underground = cave
 		else if(!completely_underground)
 		{
-			do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
+			//do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
+			do_generate_dungeons = false;
 		}
 		// Found existing dungeon underground
 		else if(found_existing && completely_underground)
@@ -4239,8 +4729,8 @@ MapBlock * ServerMap::generateBlock(
 		/*
 			Add coal
 		*/
-		u16 coal_amount = 30;
-		u16 coal_rareness = 60 / coal_amount;
+		u16 coal_amount = 60;
+		u16 coal_rareness = 120 / coal_amount;
 		if(coal_rareness == 0)
 			coal_rareness = 1;
 		if(myrand()%coal_rareness == 0)
@@ -4271,9 +4761,8 @@ MapBlock * ServerMap::generateBlock(
 		/*
 			Add iron
 		*/
-		//TODO: change to iron_amount or whatever
-		u16 iron_amount = 15;
-		u16 iron_rareness = 60 / iron_amount;
+		u16 iron_amount = 40;
+		u16 iron_rareness = 80 / iron_amount;
 		if(iron_rareness == 0)
 			iron_rareness = 1;
 		if(myrand()%iron_rareness == 0)
@@ -4330,8 +4819,17 @@ MapBlock * ServerMap::generateBlock(
 	*/
 	sector->insertBlock(block);
 	
-	// Lighting is invalid after generation.
-	block->setLightingExpired(true);
+	// Lighting is invalid after generation for surface blocks
+	if(block_type == BT_SURFACE)
+	{
+		block->setLightingExpired(true);
+		lighting_invalidated_blocks.insert(p, block);
+	}
+	// Lighting is not invalid for other blocks
+	else
+	{
+		block->setLightingExpired(false);
+	}
 	
 #if 0
 	/*
@@ -4536,7 +5034,9 @@ MapBlock * ServerMap::emergeBlock(
 	if(does_not_exist)
 	{
 		block = generateBlock(p, block, sector, changed_blocks,
-				lighting_invalidated_blocks); 
+				lighting_invalidated_blocks);
+
+		lighting_expired = block->getLightingExpired();
 	}
 
 	if(lighting_expired)
@@ -4548,6 +5048,7 @@ MapBlock * ServerMap::emergeBlock(
 		Initially update sunlight
 	*/
 	
+	if(lighting_expired)
 	{
 		core::map<v3s16, bool> light_sources;
 		bool black_air_left = false;
diff --git a/src/map.h b/src/map.h
index 1cebef634..0692f2b23 100644
--- a/src/map.h
+++ b/src/map.h
@@ -40,6 +40,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "voxel.h"
 #include "mapchunk.h"
 
+/*
+	Some exposed functions
+*/
+
+double base_rock_level_2d(u64 seed, v2f p);
+
+/*
+*/
+
 #define MAPTYPE_BASE 0
 #define MAPTYPE_SERVER 1
 #define MAPTYPE_CLIENT 2
@@ -531,6 +540,8 @@ class ServerMap : public Map
 
 	bool isSavingEnabled(){ return m_map_saving_enabled; }
 
+	u64 getSeed(){ return m_seed; }
+
 private:
 	// Seed used for all kinds of randomness
 	u64 m_seed;
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index d489ec8ac..9594b2961 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -34,6 +34,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
 		m_pos(pos),
 		changed(true),
 		is_underground(false),
+		m_lighting_expired(true),
 		m_day_night_differs(false),
 		m_objects(this)
 {
diff --git a/src/noise.cpp b/src/noise.cpp
index 63682e1e4..bc5148545 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -92,8 +92,7 @@ double noise3d(int x, int y, int z, int seed)
 	return 1.0 - (double)n/1073741824;
 }
 
-#if 0
-// This is too slow
+#if 1
 double noise2d_gradient(double x, double y, int seed)
 {
 	// Calculate the integer coordinates
@@ -118,7 +117,7 @@ double noise2d_gradient(double x, double y, int seed)
 }
 #endif
 
-#if 1
+#if 0
 double noise2d_gradient(double x, double y, int seed)
 {
 	// Calculate the integer coordinates
@@ -175,6 +174,21 @@ double noise2d_perlin(double x, double y, int seed,
 	return a;
 }
 
+double noise2d_perlin_abs(double x, double y, int seed,
+		int octaves, double persistence)
+{
+	double a = 0;
+	double f = 1.0;
+	double g = 1.0;
+	for(int i=0; i<octaves; i++)
+	{
+		a += g * fabs(noise2d_gradient(x*f, y*f, seed+i));
+		f *= 2.0;
+		g *= persistence;
+	}
+	return a;
+}
+
 double noise3d_perlin(double x, double y, double z, int seed,
 		int octaves, double persistence)
 {
@@ -190,3 +204,18 @@ double noise3d_perlin(double x, double y, double z, int seed,
 	return a;
 }
 
+double noise3d_perlin_abs(double x, double y, double z, int seed,
+		int octaves, double persistence)
+{
+	double a = 0;
+	double f = 1.0;
+	double g = 1.0;
+	for(int i=0; i<octaves; i++)
+	{
+		a += g * fabs(noise3d_gradient(x*f, y*f, z*f, seed+i));
+		f *= 2.0;
+		g *= persistence;
+	}
+	return a;
+}
+
diff --git a/src/noise.h b/src/noise.h
index 63974e86a..88b995b1e 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -32,8 +32,14 @@ double noise3d_gradient(double x, double y, double z, int seed);
 double noise2d_perlin(double x, double y, int seed,
 		int octaves, double persistence);
 
+double noise2d_perlin_abs(double x, double y, int seed,
+		int octaves, double persistence);
+
 double noise3d_perlin(double x, double y, double z, int seed,
 		int octaves, double persistence);
 
+double noise3d_perlin_abs(double x, double y, double z, int seed,
+		int octaves, double persistence);
+
 #endif
 
diff --git a/src/server.cpp b/src/server.cpp
index 24f22c6b3..e4c92e356 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -167,6 +167,26 @@ void * EmergeThread::Thread()
 						only_from_disk,
 						changed_blocks,
 						lighting_invalidated_blocks);
+				
+				/*
+					While we're at it, generate some other blocks too
+				*/
+				try
+				{
+					map.emergeBlock(
+							p+v3s16(0,1,0),
+							only_from_disk,
+							changed_blocks,
+							lighting_invalidated_blocks);
+					map.emergeBlock(
+							p+v3s16(0,-1,0),
+							only_from_disk,
+							changed_blocks,
+							lighting_invalidated_blocks);
+				}
+				catch(InvalidPositionException &e)
+				{
+				}
 			}
 
 			// If it is a dummy, block was not found on disk
@@ -208,23 +228,25 @@ void * EmergeThread::Thread()
 				Collect a list of blocks that have been modified in
 				addition to the fetched one.
 			*/
-
-			// Add all the "changed blocks" to modified_blocks
-			for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
-					i.atEnd() == false; i++)
-			{
-				MapBlock *block = i.getNode()->getValue();
-				modified_blocks.insert(block->getPos(), block);
-			}
 			
-			/*dstream<<"lighting "<<lighting_invalidated_blocks.size()
-					<<" blocks"<<std::endl;*/
+			if(lighting_invalidated_blocks.size() > 0)
+				dstream<<"lighting "<<lighting_invalidated_blocks.size()
+						<<" blocks"<<std::endl;
 			
-			//TimeTaker timer("** updateLighting");
+			// 50-100ms for single block generation
+			//TimeTaker timer("** EmergeThread updateLighting");
 			
 			// Update lighting without locking the environment mutex,
 			// add modified blocks to changed blocks
 			map.updateLighting(lighting_invalidated_blocks, modified_blocks);
+			
+			// Add all from changed_blocks to modified_blocks
+			for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
+					i.atEnd() == false; i++)
+			{
+				MapBlock *block = i.getNode()->getValue();
+				modified_blocks.insert(block->getPos(), block);
+			}
 		}
 		// If we got no block, there should be no invalidated blocks
 		else
@@ -551,7 +573,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			{
 				//TODO: Get value from somewhere
 				// Allow only one block in emerge queue
-				if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
+				//if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
+				if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
 				{
 					//dstream<<"Adding block to emerge queue"<<std::endl;
 					
@@ -1681,10 +1704,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
 		// Now answer with a TOCLIENT_INIT
 		
-		SharedBuffer<u8> reply(2+1+6);
+		SharedBuffer<u8> reply(2+1+6+8);
 		writeU16(&reply[0], TOCLIENT_INIT);
 		writeU8(&reply[2], deployed);
-		writeV3S16(&reply[3], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
+		writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
+		writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
 		// Send as reliable
 		m_con.Send(peer_id, 0, reply, true);
 
diff --git a/src/tile.cpp b/src/tile.cpp
index cfbb68249..1f4456653 100644
--- a/src/tile.cpp
+++ b/src/tile.cpp
@@ -486,7 +486,7 @@ void TextureSource::buildMainAtlas()
 	sourcelist.push_back("sand.png^mineral_iron.png");
 	
 	// Padding to disallow texture bleeding
-	s32 padding = 8;
+	s32 padding = 16;
 
 	/*
 		First pass: generate almost everything
diff --git a/src/utility.h b/src/utility.h
index 2b878b82b..ce43f26a3 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -39,6 +39,18 @@ extern const v3s16 g_26dirs[26];
 // 26th is (0,0,0)
 extern const v3s16 g_27dirs[27];
 
+inline void writeU64(u8 *data, u64 i)
+{
+	data[0] = ((i>>56)&0xff);
+	data[1] = ((i>>48)&0xff);
+	data[2] = ((i>>40)&0xff);
+	data[3] = ((i>>32)&0xff);
+	data[4] = ((i>>24)&0xff);
+	data[5] = ((i>>16)&0xff);
+	data[6] = ((i>> 8)&0xff);
+	data[7] = ((i>> 0)&0xff);
+}
+
 inline void writeU32(u8 *data, u32 i)
 {
 	data[0] = ((i>>24)&0xff);
@@ -58,6 +70,14 @@ inline void writeU8(u8 *data, u8 i)
 	data[0] = ((i>> 0)&0xff);
 }
 
+inline u64 readU64(u8 *data)
+{
+	return ((u64)data[0]<<56) | ((u64)data[1]<<48)
+		| ((u64)data[2]<<40) | ((u64)data[3]<<32)
+		| ((u64)data[4]<<24) | ((u64)data[5]<<16)
+		| ((u64)data[6]<<8) | ((u64)data[7]<<0);
+}
+
 inline u32 readU32(u8 *data)
 {
 	return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
-- 
GitLab