From 6e196c2ce4285c0aea2a5c714e842d90c1b84b43 Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Tue, 1 Feb 2011 03:06:02 +0200
Subject: [PATCH] partly working chunk-based map generator (doesn't save
 properly, spawn is pretty random)

---
 src/irrlichtwrapper.cpp |   10 +
 src/irrlichtwrapper.h   |    6 +
 src/main.cpp            |   41 +-
 src/map.cpp             | 1321 +++++++++++++++++++++++++++++++++++----
 src/map.h               |   33 +-
 src/mapblock.cpp        |   11 +
 src/mapblock.h          |    2 +
 src/mapnode.h           |    8 +
 src/mapsector.cpp       |    9 +-
 src/server.cpp          |   12 +-
 src/utility.h           |   20 +
 src/voxel.cpp           |   19 +-
 src/voxel.h             |   28 +
 13 files changed, 1382 insertions(+), 138 deletions(-)

diff --git a/src/irrlichtwrapper.cpp b/src/irrlichtwrapper.cpp
index e5cab98c6..a1d37f4bd 100644
--- a/src/irrlichtwrapper.cpp
+++ b/src/irrlichtwrapper.cpp
@@ -5,6 +5,7 @@
 
 IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device)
 {
+	m_running = true;
 	m_main_thread = get_current_thread_id();
 	m_device_mutex.Init();
 	m_device = device;
@@ -35,6 +36,11 @@ void IrrlichtWrapper::Run()
 	}
 }
 
+void IrrlichtWrapper::Shutdown(bool shutdown)
+{
+	m_running = !shutdown;
+}
+
 textureid_t IrrlichtWrapper::getTextureId(const std::string &name)
 {
 	u32 id = m_namecache.getId(name);
@@ -73,6 +79,10 @@ video::ITexture* IrrlichtWrapper::getTexture(const TextureSpec &spec)
 	}
 	else
 	{
+		// If irrlicht has shut down, just return NULL
+		if(m_running == false)
+			return NULL;
+
 		// We're gonna ask the result to be put into here
 		ResultQueue<TextureSpec, video::ITexture*, u8, u8> result_queue;
 		
diff --git a/src/irrlichtwrapper.h b/src/irrlichtwrapper.h
index 225ae3402..965d01208 100644
--- a/src/irrlichtwrapper.h
+++ b/src/irrlichtwrapper.h
@@ -132,11 +132,15 @@ class IrrlichtWrapper : public IIrrlichtWrapper
 	/*
 		These are called from the main thread
 	*/
+
 	IrrlichtWrapper(IrrlichtDevice *device);
 	
 	// Run queued tasks
 	void Run();
 
+	// Shutdown wrapper; this disables queued texture fetching
+	void Shutdown(bool shutdown);
+
 	/*
 		These are called from other threads
 	*/
@@ -181,6 +185,8 @@ class IrrlichtWrapper : public IIrrlichtWrapper
 	/*
 		Members
 	*/
+
+	bool m_running;
 	
 	// The id of the thread that can (and has to) use irrlicht directly
 	threadid_t m_main_thread;
diff --git a/src/main.cpp b/src/main.cpp
index dc4716fab..87deefee4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -288,7 +288,7 @@ FEATURE: Map generator version 2
 	  where some minerals are found
 	- Create a system that allows a huge amount of different "map
 	  generator modules/filters"
-	  
+
 FEATURE: The map could be generated procedually:
       - This would need the map to be generated in larger pieces
 	    - How large? How do they connect to each other?
@@ -296,7 +296,10 @@ FEATURE: The map could be generated procedually:
 		- Lighting would not have to be necessarily calculated until
 		  the blocks are actually needed - it would be quite fast
 		- Something like 64*64*16 MapBlocks?
-		- TODO: Separate lighting and block generation
+		  - No, MapSectors. And as much as it is efficient to do,
+		    64x64 might be too much.
+        - FIXME: This is currently halfway done and the generator is
+		  fairly broken
       * Make the stone level with a heightmap
 	  * Carve out stuff in the stone
 	  * Dump dirt all around, and simulate it falling off steep
@@ -311,16 +314,25 @@ FEATURE: The map could be generated procedually:
 			parameter field is free for this.
 		- Simulate rock falling from cliffs when water has removed
 		  enough solid rock from the bottom
-TODO: Lazy lighting updates:
-    - Set updateLighting to ignore MapBlocks with expired lighting,
-	  except the blocks specified to it
-	- When a MapBlock is generated, lighting expires in all blocks
-	  touching it (26 blocks + self)
-	- When a lighting-wise valid MapBlock is needed and lighting of it
-	  has expired, what to do?
 
 Doing now:
 ----------
+# maybe done
+* not done
+
+* Remove all kinds of systems that are made redundant by the new map
+  generator
+  - Sector heightmaps? At least they should be made redundant.
+  - Sector objects
+* Do something about AttributeDatabase/List being too slow
+* Save chunk metadata on disk
+* Change water side textures so that buggy water doesn't look bad
+* Make server find the spawning place from the real map data, not from
+  the heightmap
+* only_from_disk doesn't work that well anymore
+* Make the generator to run in background and not blocking block
+  placement and transfer
+* Fix the strange mineral occurences
 
 ======================================================================
 
@@ -1886,6 +1898,9 @@ int main(int argc, char *argv[])
 	*/
 	{
 
+	// This is set to true at the end of the scope
+	g_irrlicht->Shutdown(false);
+
 	/*
 		Draw "Loading" screen
 	*/
@@ -3017,6 +3032,14 @@ int main(int argc, char *argv[])
 
 	delete quick_inventory;
 
+	/*
+		Disable texture fetches and other stuff that is queued
+		to be processed by the main loop.
+
+		This has to be done before client goes out of scope.
+	*/
+	g_irrlicht->Shutdown(true);
+
 	} // client and server are deleted at this point
 
 	} //try
diff --git a/src/map.cpp b/src/map.cpp
index cc1a6d638..63fccc432 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -133,6 +133,18 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
 	}
 }
 
+/*MapBlock * Map::getBlockCreate(v3s16 p3d)
+{
+	v2s16 p2d(p3d.X, p3d.Z);
+	MapSector * sector = getSectorCreate(p2d);
+	assert(sector);
+	MapBlock *block = sector->getBlockNoCreate(p3d.Y);
+	if(block)
+		return block;
+	block = sector->createBlankBlock(p3d.Y);
+	return block;
+}*/
+
 f32 Map::getGroundHeight(v2s16 p, bool generate)
 {
 	try{
@@ -1724,7 +1736,8 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 	//m_chunksize = 64;
 	//m_chunksize = 16;
 	//m_chunksize = 8;
-	m_chunksize = 2;
+	m_chunksize = 4;
+	//m_chunksize = 2;
 
 	/*
 		Experimental and debug stuff
@@ -1739,6 +1752,7 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 		PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
 		PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
 
+#if 0
 		/*
 			NOTE: BEWARE: Too big amount of these will make map generation
 			slow. Especially those that are read by every block emerge.
@@ -1749,13 +1763,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 			15000 points: 40ms
 		*/
 		
-		for(u32 i=0; i<5000; i++)
+		for(u32 i=0; i<500; i++)
 		{
 			/*u32 lim = MAP_GENERATION_LIMIT;
 			if(i < 400)
 				lim = 2000;*/
 
-			u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
+			u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
 
 			v3s16 p(
 				-lim + myrand()%(lim*2),
@@ -1788,13 +1802,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 			list_plants_amount->addPoint(p, Attribute(plants_amount));
 		}
 
-		for(u32 i=0; i<1000; i++)
+		for(u32 i=0; i<500; i++)
 		{
 			/*u32 lim = MAP_GENERATION_LIMIT;
 			if(i < 400)
 				lim = 2000;*/
 
-			u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
+			u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
 
 			v3s16 p(
 				-lim + myrand()%(lim*2),
@@ -1819,13 +1833,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 			list_caves_amount->addPoint(p, Attribute(caves_amount));
 		}
 		
-		for(u32 i=0; i<5000; i++)
+		for(u32 i=0; i<500; i++)
 		{
 			/*u32 lim = MAP_GENERATION_LIMIT;
 			if(i < 400)
 				lim = 2000;*/
 
-			u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
+			u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
 
 			v3s16 p(
 				-lim + (myrand()%(lim*2)),
@@ -1896,10 +1910,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 			list_randmax->addPoint(p, Attribute(randmax));
 			list_randfactor->addPoint(p, Attribute(randfactor));
 		}
-
-		/*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
-		list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
-		list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
+#endif
+		
+		// Add only one entry
+		list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
+		list_randmax->addPoint(v3s16(0,0,0), Attribute(30));
+		list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
 
 		// Easy spawn point
 		/*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
@@ -2010,6 +2026,7 @@ ServerMap::~ServerMap()
 	}
 }
 
+#if 0
 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
 {
 	// Return if chunk already exists
@@ -2054,58 +2071,998 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
 					<<"("<<sectorpos.X<<","<<sectorpos.Y<<")"
 					<<std::endl;*/
 
-			// Generate sector
-			ServerMapSector *sector = generateSector(sectorpos);
+			// Generate sector
+			ServerMapSector *sector = generateSector(sectorpos);
+
+			/*
+				Generate main blocks of sector
+			*/
+			s16 d = 8;
+			for(s16 y2=-d/2; y2<d/2; y2++)
+			{
+				v3s16 p(x,y2,y);
+				
+				// Check that the block doesn't exist already
+				if(sector->getBlockNoCreateNoEx(y2))
+					continue;
+				
+				generateBlock(p, NULL, sector, changed_blocks,
+						lighting_invalidated_blocks);
+
+				generated_block_count++;
+			}
+		}
+	}
+
+	dstream<<"generateChunkRaw generated "<<generated_block_count
+			<<" blocks"<<std::endl;
+
+	/*{
+		TimeTaker timer2("generateChunkRaw() lighting");
+		// Update lighting
+		core::map<v3s16, MapBlock*> lighting_modified_blocks;
+		updateLighting(lighting_invalidated_blocks, lighting_modified_blocks);
+	}*/
+	
+	// Add chunk meta information
+	chunk = new MapChunk();
+	m_chunks.insert(chunkpos, chunk);
+	return chunk;
+}
+
+MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
+{
+	/*
+		Generate chunk and neighbors
+	*/
+	for(s16 x=-1; x<=1; x++)
+	for(s16 y=-1; y<=1; y++)
+	{
+		generateChunkRaw(chunkpos + v2s16(x,y));
+	}
+
+	/*
+		Get chunk
+	*/
+	MapChunk *chunk = getChunk(chunkpos);
+	assert(chunk);
+	// Set non-volatile
+	chunk->setIsVolatile(false);
+	// Return it
+	return chunk;
+}
+#endif
+
+MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
+{
+	dstream<<"WARNING: No-op "<<__FUNCTION_NAME<<" called"<<std::endl;
+	return NULL;
+}
+
+/*
+	Some helper functions
+*/
+
+s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
+{
+	v3s16 em = vmanip.m_area.getExtent();
+	s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
+	s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
+	u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+	s16 y;
+	for(y=y_nodes_max; y>=y_nodes_min; y--)
+	{
+		MapNode &n = vmanip.m_data[i];
+		if(content_walkable(n.d))
+			break;
+			
+		vmanip.m_area.add_y(em, i, -1);
+	}
+	if(y >= y_nodes_min)
+		return y;
+	else
+		return y_nodes_min;
+}
+
+void make_tree(VoxelManipulator &vmanip, v3s16 p0)
+{
+	MapNode treenode(CONTENT_TREE);
+	MapNode leavesnode(CONTENT_LEAVES);
+
+	s16 trunk_h = myrand_range(2, 6);
+	v3s16 p1 = p0;
+	for(s16 ii=0; ii<trunk_h; ii++)
+	{
+		if(vmanip.m_area.contains(p1))
+			vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+		p1.Y++;
+	}
+	
+	// p1 is now the last piece of the trunk
+	p1.Y -= 1;
+
+	VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
+	SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
+	for(s32 i=0; i<leaves_a.getVolume(); i++)
+		leaves_d[i] = 0;
+	
+	// Force leaves at near the end of the trunk
+	{
+		s16 d = 1;
+		for(s16 z=-d; z<=d; z++)
+		for(s16 y=-d; y<=d; y++)
+		for(s16 x=-d; x<=d; x++)
+		{
+			leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
+		}
+	}
+	
+	// Add leaves randomly
+	for(u32 iii=0; iii<7; iii++)
+	{
+		s16 d = 1;
+
+		v3s16 p(
+			myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
+			myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
+			myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+		);
+		
+		for(s16 z=0; z<=d; z++)
+		for(s16 y=0; y<=d; y++)
+		for(s16 x=0; x<=d; x++)
+		{
+			leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
+		}
+	}
+	
+	// Blit leaves to vmanip
+	for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
+	for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
+	for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
+	{
+		v3s16 p(x,y,z);
+		p += p1;
+		if(vmanip.m_area.contains(p) == false)
+			continue;
+		u32 vi = vmanip.m_area.index(p);
+		if(vmanip.m_data[vi].d != CONTENT_AIR)
+			continue;
+		u32 i = leaves_a.index(x,y,z);
+		if(leaves_d[i] == 1)
+			vmanip.m_data[vi] = leavesnode;
+	}
+}
+
+MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
+{
+	TimeTaker timer("generateChunk()");
+	
+	// The distance how far into the neighbors the generator is allowed to go
+	s16 max_spread_amount = m_chunksize * MAP_BLOCKSIZE / 2 + 2;
+	// Minimum amount of space left on sides for mud to fall in
+	s16 min_mud_fall_space = 2;
+	// Maximum diameter of stone obstacles in X and Z
+	s16 stone_obstacle_max_size = m_chunksize*MAP_BLOCKSIZE/2;
+	assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
+	
+	s16 y_blocks_min = -4;
+	s16 y_blocks_max = 3;
+	s16 h_blocks = y_blocks_max - y_blocks_min + 1;
+	s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
+	s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
+
+	v2s16 sectorpos_base = chunk_to_sector(chunkpos);
+	s16 sectorpos_base_size = m_chunksize;
+	v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
+	s16 sectorpos_bigbase_size = m_chunksize * 3;
+
+	v3s16 bigarea_blocks_min(
+		sectorpos_bigbase.X,
+		y_blocks_min,
+		sectorpos_bigbase.Y
+	);
+
+	v3s16 bigarea_blocks_max(
+		sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
+		y_blocks_min + sectorpos_bigbase_size - 1,
+		sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
+	);
+
+	/*
+		Create the whole area of this and the neighboring chunks
+	*/
+	{
+		TimeTaker timer("generateChunk() create area");
+		
+		for(s16 x=0; x<sectorpos_bigbase_size; x++)
+		for(s16 z=0; z<sectorpos_bigbase_size; z++)
+		{
+			v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
+			ServerMapSector *sector = createSector(sectorpos);
+			assert(sector);
+
+			for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
+			{
+				v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
+				MapBlock *block = createBlock(blockpos);
+
+				// Lighting won't be calculated
+				//block->setLightingExpired(true);
+				// Lighting will be calculated
+				block->setLightingExpired(false);
+
+				/*
+					TODO: Do this better.
+					Block gets sunlight if this is true.
+
+					This should be set to true when the top side of a block
+					is completely exposed to the sky.
+				*/
+				block->setIsUnderground(y != y_blocks_max);
+			}
+		}
+	}
+
+	/*
+		Now we have a big empty area.
+
+		Make a ManualMapVoxelManipulator that contains this and the
+		neighboring chunks
+	*/
+
+	ManualMapVoxelManipulator vmanip(this);
+	// Add the area we just generated
+	{
+		TimeTaker timer("generateChunk() initialEmerge");
+		vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
+	}
+
+	TimeTaker timer_generate("generateChunk() generate");
+
+	/*
+		Generate general ground level to full area
+	*/
+
+	for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
+	for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
+	{
+		// Node position
+		v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		/*
+			Skip of already generated
+		*/
+		{
+			v3s16 p(p2d.X, y_nodes_min, p2d.Y);
+			if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
+				continue;
+		}
+
+		// Ground height at this point
+		float surface_y_f = 0.0;
+		/*
+			A hack to get the ground height from the sector.
+			Do this better.
+		*/
+		{
+			v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
+			v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
+			MapSector *sector = getSectorNoGenerate(sectorpos);
+			assert(sector);
+			float h = sector->getGroundHeight(sector_relpos);
+			if(h > GROUNDHEIGHT_VALID_MINVALUE)
+				surface_y_f = h;
+			else
+				dstream<<"WARNING: "<<__FUNCTION_NAME
+				<<": sector->getGroundHeight returned bad height"<<std::endl;
+		}
+		// Convert to integer
+		s16 surface_y = (s16)surface_y_f;
+
+		/*
+			Fill ground with stone
+		*/
+		{
+			// Use fast index incrementing
+			v3s16 em = vmanip.m_area.getExtent();
+			u32 i = 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++)
+			{
+				vmanip.m_data[i].d = CONTENT_STONE;
+
+				vmanip.m_area.add_y(em, i, 1);
+			}
+		}
+	}
+
+	/*
+		Add some random stone obstacles
+	*/
+
+	for(u32 ri=0; ri<10; ri++)
+	{
+		// The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
+		v3s16 ob_size(
+			myrand_range(5, stone_obstacle_max_size),
+			myrand_range(0, 15),
+			myrand_range(5, stone_obstacle_max_size)
+		);
+		v2s16 ob_place(
+			myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
+			myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
+		);
+		
+		for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
+		for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
+		{
+			// Node position in 2d
+			v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
+			
+			// Find stone ground level
+			// (ignore everything else than mud in already generated chunks)
+			// and mud amount over the stone level
+			s16 surface_y = 0;
+			s16 mud_amount = 0;
+			{
+				v3s16 em = vmanip.m_area.getExtent();
+				u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+				s16 y;
+				// Go to ground level
+				for(y=y_nodes_max; y>=y_nodes_min; y--)
+				{
+					MapNode &n = vmanip.m_data[i];
+					/*if(content_walkable(n.d)
+							&& n.d != CONTENT_MUD
+							&& n.d != CONTENT_GRASS)
+						break;*/
+					if(n.d == CONTENT_STONE)
+						break;
+
+					if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS)
+						mud_amount++;
+						
+					vmanip.m_area.add_y(em, i, -1);
+				}
+				if(y >= y_nodes_min)
+					surface_y = y;
+				else
+					surface_y = y_nodes_min;
+			}
+
+
+			/*
+				Add stone on ground
+			*/
+			{
+				v3s16 em = vmanip.m_area.getExtent();
+				s16 y_start = surface_y+1;
+				u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+				s16 y;
+				// Add stone
+				s16 count = 0;
+				for(y=y_start; y<=y_nodes_max; y++)
+				{
+					MapNode &n = vmanip.m_data[i];
+					n.d = CONTENT_STONE;
+					count++;
+					if(count >= ob_size.Y)
+						break;
+						
+					vmanip.m_area.add_y(em, i, 1);
+				}
+				// Add mud
+				count = 0;
+				for(; y<=y_nodes_max; y++)
+				{
+					MapNode &n = vmanip.m_data[i];
+					n.d = CONTENT_MUD;
+					count++;
+					if(count >= mud_amount)
+						break;
+						
+					vmanip.m_area.add_y(em, i, 1);
+				}
+			}
+
+		}
+	}
+
+	/*
+		Make dungeons
+	*/
+	for(u32 jj=0; jj<2; jj++)
+	{
+		s16 max_tunnel_diameter = 8;
+
+		// Allowed route area size in nodes
+		v3s16 ar(
+			sectorpos_base_size*MAP_BLOCKSIZE,
+			h_blocks*MAP_BLOCKSIZE,
+			sectorpos_base_size*MAP_BLOCKSIZE
+		);
+
+		// Area starting point in nodes
+		v3s16 of(
+			sectorpos_base.X*MAP_BLOCKSIZE,
+			y_blocks_min*MAP_BLOCKSIZE,
+			sectorpos_base.Y*MAP_BLOCKSIZE
+		);
+
+		// Allow a bit more
+		//(this should be more than the maximum radius of the tunnel)
+		s16 more = max_spread_amount - max_tunnel_diameter/2 - 1;
+		ar += v3s16(1,0,1) * more * 2;
+		of -= v3s16(1,0,1) * more;
+		
+		// Randomize starting position
+		v3f orp(
+			(float)(myrand()%ar.X)+0.5,
+			(float)(myrand()%ar.Y)+0.5,
+			(float)(myrand()%ar.Z)+0.5
+		);
+
+		MapNode airnode(CONTENT_AIR);
+		
+		/*
+			Generate some tunnel starting from orp
+		*/
+
+		for(u16 j=0; j<10; j++)
+		{
+			/*v3f rp(
+				(float)(myrand()%ar.X)+0.5,
+				(float)(myrand()%ar.Y)+0.5,
+				(float)(myrand()%ar.Z)+0.5
+			);
+			v3f vec = rp - orp;*/
+			
+			v3s16 maxlen(60, 10, 60);
+			v3f vec(
+				(float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
+				(float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
+				(float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
+			);
+			v3f rp = orp + vec;
+			if(rp.X < 0)
+				rp.X = 0;
+			else if(rp.X >= ar.X)
+				rp.X = ar.X;
+			if(rp.Y < 0)
+				rp.Y = 0;
+			else if(rp.Y >= ar.Y)
+				rp.Y = ar.Y;
+			if(rp.Z < 0)
+				rp.Z = 0;
+			else if(rp.Z >= ar.Z)
+				rp.Z = ar.Z;
+			vec = rp - orp;
+
+			// Randomize size
+			s16 min_d = 0;
+			s16 max_d = max_tunnel_diameter;
+			s16 rs = myrand_range(min_d, max_d);
+			
+			for(float f=0; f<1.0; f+=1.0/vec.getLength())
+			{
+				v3f fp = orp + vec * f;
+				v3s16 cp(fp.X, fp.Y, fp.Z);
+				s16 d0 = -rs/2;
+				s16 d1 = d0 + rs - 1;
+				for(s16 z0=d0; z0<=d1; z0++)
+				{
+					s16 si = rs - abs(z0);
+					for(s16 x0=-si; x0<=si-1; x0++)
+					{
+						s16 si2 = rs - abs(x0);
+						for(s16 y0=-si2+1; y0<=si2-1; y0++)
+						{
+							s16 z = cp.Z + z0;
+							s16 y = cp.Y + y0;
+							s16 x = cp.X + x0;
+							v3s16 p(x,y,z);
+							/*if(isInArea(p, ar) == false)
+								continue;*/
+							// Check only height
+							if(y < 0 || y >= ar.Y)
+								continue;
+							p += of;
+							
+							assert(vmanip.m_area.contains(p));
+							
+							// Just set it to air, it will be changed to
+							// water afterwards
+							u32 i = vmanip.m_area.index(p);
+							vmanip.m_data[i] = airnode;
+						}
+					}
+				}
+			}
+
+			orp = rp;
+		}
+	
+	}
+
+	/*
+		Make ore veins
+	*/
+	for(u32 jj=0; jj<1000; jj++)
+	{
+		s16 max_vein_diameter = 3;
+
+		// Allowed route area size in nodes
+		v3s16 ar(
+			sectorpos_base_size*MAP_BLOCKSIZE,
+			h_blocks*MAP_BLOCKSIZE,
+			sectorpos_base_size*MAP_BLOCKSIZE
+		);
+
+		// Area starting point in nodes
+		v3s16 of(
+			sectorpos_base.X*MAP_BLOCKSIZE,
+			y_blocks_min*MAP_BLOCKSIZE,
+			sectorpos_base.Y*MAP_BLOCKSIZE
+		);
+
+		// Allow a bit more
+		//(this should be more than the maximum radius of the tunnel)
+		s16 more = max_spread_amount - max_vein_diameter/2 - 1;
+		ar += v3s16(1,0,1) * more * 2;
+		of -= v3s16(1,0,1) * more;
+		
+		// Randomize starting position
+		v3f orp(
+			(float)(myrand()%ar.X)+0.5,
+			(float)(myrand()%ar.Y)+0.5,
+			(float)(myrand()%ar.Z)+0.5
+		);
+
+		// Randomize mineral
+		u8 mineral = myrand_range(1, MINERAL_COUNT-1);
+
+		/*
+			Generate some vein starting from orp
+		*/
+
+		for(u16 j=0; j<2; j++)
+		{
+			/*v3f rp(
+				(float)(myrand()%ar.X)+0.5,
+				(float)(myrand()%ar.Y)+0.5,
+				(float)(myrand()%ar.Z)+0.5
+			);
+			v3f vec = rp - orp;*/
+			
+			v3s16 maxlen(10, 10, 10);
+			v3f vec(
+				(float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
+				(float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
+				(float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
+			);
+			v3f rp = orp + vec;
+			if(rp.X < 0)
+				rp.X = 0;
+			else if(rp.X >= ar.X)
+				rp.X = ar.X;
+			if(rp.Y < 0)
+				rp.Y = 0;
+			else if(rp.Y >= ar.Y)
+				rp.Y = ar.Y;
+			if(rp.Z < 0)
+				rp.Z = 0;
+			else if(rp.Z >= ar.Z)
+				rp.Z = ar.Z;
+			vec = rp - orp;
+
+			// Randomize size
+			s16 min_d = 0;
+			s16 max_d = max_vein_diameter;
+			s16 rs = myrand_range(min_d, max_d);
+			
+			for(float f=0; f<1.0; f+=1.0/vec.getLength())
+			{
+				v3f fp = orp + vec * f;
+				v3s16 cp(fp.X, fp.Y, fp.Z);
+				s16 d0 = -rs/2;
+				s16 d1 = d0 + rs - 1;
+				for(s16 z0=d0; z0<=d1; z0++)
+				{
+					s16 si = rs - abs(z0);
+					for(s16 x0=-si; x0<=si-1; x0++)
+					{
+						s16 si2 = rs - abs(x0);
+						for(s16 y0=-si2+1; y0<=si2-1; y0++)
+						{
+							// Don't put mineral to every place
+							if(myrand()%5 != 0)
+								continue;
+
+							s16 z = cp.Z + z0;
+							s16 y = cp.Y + y0;
+							s16 x = cp.X + x0;
+							v3s16 p(x,y,z);
+							/*if(isInArea(p, ar) == false)
+								continue;*/
+							// Check only height
+							if(y < 0 || y >= ar.Y)
+								continue;
+							p += of;
+							
+							assert(vmanip.m_area.contains(p));
+							
+							// Just set it to air, it will be changed to
+							// water afterwards
+							u32 i = vmanip.m_area.index(p);
+							MapNode *n = &vmanip.m_data[i];
+							if(n->d == CONTENT_STONE)
+								n->param = mineral;
+						}
+					}
+				}
+			}
+
+			orp = rp;
+		}
+	
+	}
+
+	/*
+		Add mud to the central chunk
+	*/
+	
+	for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
+	for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
+	{
+		// Node position in 2d
+		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		// Find ground level
+		s16 surface_y = find_ground_level(vmanip, p2d);
+
+		/*
+			Add mud on ground
+		*/
+		{
+			s16 mudcount = 0;
+			v3s16 em = vmanip.m_area.getExtent();
+			s16 y_start = surface_y+1;
+			u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+			for(s16 y=y_start; y<=y_nodes_max; y++)
+			{
+				MapNode &n = vmanip.m_data[i];
+				n.d = CONTENT_MUD;
+				mudcount++;
+				if(mudcount >= 3)
+					break;
+					
+				vmanip.m_area.add_y(em, i, 1);
+			}
+		}
+
+	}
+
+	/*
+		Flow mud away from steep edges
+	*/
+
+	// Iterate a few times
+	for(s16 k=0; k<4; k++)
+	{
+
+	for(s16 x=0-max_spread_amount+1;
+			x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
+			x++)
+	for(s16 z=0-max_spread_amount+1;
+			z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
+			z++)
+	{
+		// Node position in 2d
+		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		v3s16 em = vmanip.m_area.getExtent();
+		u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+		s16 y;
+		// Go to ground level
+		for(y=y_nodes_max; y>=y_nodes_min; y--)
+		{
+			MapNode &n = vmanip.m_data[i];
+			//if(n.d != CONTENT_AIR)
+			if(content_walkable(n.d))
+				break;
+				
+			vmanip.m_area.add_y(em, i, -1);
+		}
+
+		// If not mud, do nothing to it
+		MapNode *n = &vmanip.m_data[i];
+		if(n->d != CONTENT_MUD)
+			continue;
+
+		v3s16 dirs4[4] = {
+			v3s16(0,0,1), // back
+			v3s16(1,0,0), // right
+			v3s16(0,0,-1), // front
+			v3s16(-1,0,0), // left
+		};
+
+		// Drop mud on side
+		
+		for(u32 di=0; di<4; di++)
+		{
+			v3s16 dirp = dirs4[di];
+			u32 i2 = i;
+			// Check that side is air
+			vmanip.m_area.add_p(em, i2, dirp);
+			MapNode *n2 = &vmanip.m_data[i2];
+			if(content_walkable(n2->d))
+				continue;
+			// Check that under side is air
+			vmanip.m_area.add_y(em, i2, -1);
+			n2 = &vmanip.m_data[i2];
+			if(content_walkable(n2->d))
+				continue;
+			// Loop further down until not air
+			do{
+				vmanip.m_area.add_y(em, i2, -1);
+				n2 = &vmanip.m_data[i2];
+			}while(content_walkable(n2->d) == false);
+			// Loop one up so that we're in air
+			vmanip.m_area.add_y(em, i2, 1);
+			n2 = &vmanip.m_data[i2];
+
+			// Move mud to new place
+			*n2 = *n;
+			// Set old place to be air
+			*n = MapNode(CONTENT_AIR);
+
+			#if 0
+			// Switch mud and other and change mud source to air
+			//MapNode tempnode = *n2;
+			*n2 = *n;
+			//*n = tempnode;
+			// Force old mud position to be air
+			n->d = CONTENT_AIR;
+			#endif
+
+			// Done
+			break;
+		}
+	}
+	
+	}
+
+	/*
+		Add water to the central chunk (and a bit more)
+	*/
+	
+	for(s16 x=0-max_spread_amount;
+			x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
+			x++)
+	for(s16 z=0-max_spread_amount;
+			z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
+			z++)
+	{
+		// Node position in 2d
+		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		// Find ground level
+		s16 surface_y = find_ground_level(vmanip, p2d);
+
+		// If ground level is over water level, skip.
+		if(surface_y > WATER_LEVEL)
+			continue;
+
+		/*
+			Add water on ground
+		*/
+		{
+			v3s16 em = vmanip.m_area.getExtent();
+			s16 y_start = WATER_LEVEL;
+			u8 light = LIGHT_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];
+				
+				// Fill gaps inside water, too
+				if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
+						&& n->d != CONTENT_WATER)
+					break;
+				
+				n->d = CONTENT_WATERSOURCE;
+				n->setLight(LIGHTBANK_DAY, light);
+
+				/*
+					Add to transforming liquid queue (in case it'd
+					start flowing)
+				*/
+				v3s16 p = v3s16(p2d.X, y, p2d.Y);
+				m_transforming_liquid.push_back(p);
+				
+				// Next one
+				vmanip.m_area.add_y(em, i, -1);
+				if(light > 0)
+					light--;
+			}
+		}
+
+	}
+
+	/*
+		Plant some trees
+	*/
+	{
+		u32 tree_max = 100;
+		
+		u32 count = myrand_range(0, tree_max);
+		for(u32 i=0; i<count; i++)
+		{
+			s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
+			s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
+			x += sectorpos_base.X*MAP_BLOCKSIZE;
+			z += sectorpos_base.Y*MAP_BLOCKSIZE;
+			s16 y = find_ground_level(vmanip, v2s16(x,z));
+			// Don't make a tree under water level
+			if(y < WATER_LEVEL)
+				continue;
+			v3s16 p(x,y+1,z);
+			// Make a tree
+			make_tree(vmanip, p);
+		}
+	}
+
+	/*
+		Grow grass
+	*/
+
+	for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
+	for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)
+	{
+		// Node position in 2d
+		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		/*
+			Find the lowest surface to which enough light ends up
+			to make grass grow.
+
+			Basically just wait until not air and not leaves.
+		*/
+		s16 surface_y = 0;
+		{
+			v3s16 em = vmanip.m_area.getExtent();
+			u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+			s16 y;
+			// Go to ground level
+			for(y=y_nodes_max; y>=y_nodes_min; y--)
+			{
+				MapNode &n = vmanip.m_data[i];
+				if(n.d != CONTENT_AIR
+						&& n.d != CONTENT_LEAVES)
+					break;
+				vmanip.m_area.add_y(em, i, -1);
+			}
+			if(y >= y_nodes_min)
+				surface_y = y;
+			else
+				surface_y = y_nodes_min;
+		}
+		
+		u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
+		MapNode *n = &vmanip.m_data[i];
+		if(n->d == CONTENT_MUD)
+			n->d = CONTENT_GRASS;
+	}
+
+	/*
+		Handle lighting
+	*/
+
+	core::map<v3s16, bool> light_sources;
+
+	/*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
+	for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
+	for(s16 x=0-max_spread_amount;
+			x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
+			x++)
+	for(s16 z=0-max_spread_amount;
+			z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
+			z++)
+	{
+		// Node position in 2d
+		v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+		
+		/*
+			Apply initial sunlight
+		*/
+		{
+			v3s16 em = vmanip.m_area.getExtent();
+			s16 y_start = y_nodes_max;
+			u8 light = LIGHT_SUN;
+			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--;
+				}
 
-			/*
-				Generate main blocks of sector
-			*/
-			s16 d = 8;
-			for(s16 y2=-d/2; y2<d/2; y2++)
-			{
-				v3s16 p(x,y2,y);
+				n->setLight(LIGHTBANK_DAY, light);
+				n->setLight(LIGHTBANK_NIGHT, 0);
 				
-				// Check that the block doesn't exist already
-				if(sector->getBlockNoCreateNoEx(y2))
-					continue;
+				if(light != 0)
+				{
+					// Insert light source
+					light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
+				}
 				
-				generateBlock(p, NULL, sector, changed_blocks,
-						lighting_invalidated_blocks);
-
-				generated_block_count++;
+				// Increment index by y
+				vmanip.m_area.add_y(em, i, -1);
 			}
 		}
 	}
 
-	dstream<<"generateChunkRaw generated "<<generated_block_count
-			<<" blocks"<<std::endl;
-
+	// Spread light around
 	{
-		TimeTaker timer2("generateChunkRaw() lighting");
-		// Update lighting
-		core::map<v3s16, MapBlock*> lighting_modified_blocks;
-		updateLighting(lighting_invalidated_blocks, lighting_modified_blocks);
+		TimeTaker timer("generateChunk() spreadLight");
+		vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
 	}
 	
-	// Add chunk meta information
-	chunk = new MapChunk();
-	m_chunks.insert(chunkpos, chunk);
-	return chunk;
-}
+	/*
+		Generation ended
+	*/
+
+	timer_generate.stop();
 
-MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
-{
 	/*
-		Generate chunk and neighbors
+		Blit generated stuff to map
+	*/
+	core::map<v3s16, MapBlock*> modified_blocks;
+	{
+		TimeTaker timer("generateChunk() blitBackAll");
+		vmanip.blitBackAll(&modified_blocks);
+	}
+	/*
+		Update day/night difference cache of the MapBlocks
+	*/
+	{
+		for(core::map<v3s16, MapBlock*>::Iterator i = modified_blocks.getIterator();
+				i.atEnd() == false; i++)
+		{
+			MapBlock *block = i.getNode()->getValue();
+			block->updateDayNightDiff();
+		}
+	}
+
+	
+	/*
+		Create chunks and set them volatile
 	*/
+
 	for(s16 x=-1; x<=1; x++)
 	for(s16 y=-1; y<=1; y++)
 	{
-		generateChunkRaw(chunkpos + v2s16(x,y));
+		v2s16 chunkpos0 = chunkpos + v2s16(x,y);
+		// Add chunk meta information
+		MapChunk *chunk = getChunk(chunkpos);
+		if(chunk == NULL)
+		{
+			chunk = new MapChunk();
+			m_chunks.insert(chunkpos0, chunk);
+		}
+		chunk->setIsVolatile(true);
 	}
 
 	/*
-		Get chunk
+		Set central chunk non-volatile and return it
 	*/
 	MapChunk *chunk = getChunk(chunkpos);
 	assert(chunk);
@@ -2115,6 +3072,7 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
 	return chunk;
 }
 
+#if 0
 ServerMapSector * ServerMap::generateSector(v2s16 p2d)
 {
 	DSTACK("%s: p2d=(%d,%d)",
@@ -2131,7 +3089,7 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d)
 	*/
 	if(m_heightmap == NULL)
 	{
-		throw InvalidPositionException("emergeSector(): no heightmap");
+		throw InvalidPositionException("generateSector(): no heightmap");
 	}
 
 	/*
@@ -2141,7 +3099,7 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d)
 	|| p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 	|| p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 	|| p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
-		throw InvalidPositionException("emergeSector(): pos. over limit");
+		throw InvalidPositionException("generateSector(): pos. over limit");
 
 	/*
 		Generate sector and heightmaps
@@ -2209,8 +3167,8 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d)
 	
 #if 0
 	{
-		//dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
-		//TimeTaker attrtimer("emergeSector() attribute fetch");
+		//dstream<<"generateSector(): Reading point attribute lists"<<std::endl;
+		//TimeTaker attrtimer("generateSector() attribute fetch");
 		
 		// Get plant amount from attributes
 		PointAttributeList *palist = m_padb.getList("plants_amount");
@@ -2345,8 +3303,9 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d)
 	
 	return sector;
 }
+#endif
 
-MapSector * ServerMap::emergeSector(v2s16 p2d)
+ServerMapSector * ServerMap::createSector(v2s16 p2d)
 {
 	DSTACK("%s: p2d=(%d,%d)",
 			__FUNCTION_NAME,
@@ -2355,24 +3314,116 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 	/*
 		Check if it exists already in memory
 	*/
-	MapSector *sector = getSectorNoGenerateNoEx(p2d);
+	ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
 	if(sector != NULL)
 		return sector;
 	
 	/*
-		Try to load it from disk
+		Try to load it from disk (with blocks)
 	*/
 	if(loadSectorFull(p2d) == true)
 	{
-		MapSector *sector = getSectorNoGenerateNoEx(p2d);
+		ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
 		if(sector == NULL)
 		{
-			dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
+			dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
 			throw InvalidPositionException("");
 		}
 		return sector;
 	}
 
+	/*
+		If there is no master heightmap, throw.
+	*/
+	if(m_heightmap == NULL)
+	{
+		throw InvalidPositionException("createSector(): no heightmap");
+	}
+
+	/*
+		Do not create over-limit
+	*/
+	if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+	|| p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+	|| p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+	|| p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+		throw InvalidPositionException("createSector(): pos. over limit");
+
+	/*
+		Generate blank sector
+	*/
+	
+	// Number of heightmaps in sector in each direction
+	u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
+
+	// Heightmap side width
+	s16 hm_d = MAP_BLOCKSIZE / hm_split;
+
+	sector = new ServerMapSector(this, p2d, hm_split);
+	
+	// Sector position on map in nodes
+	v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
+
+	/*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
+			" heightmaps and objects"<<std::endl;*/
+	
+	/*
+		Generate sector heightmap
+	*/
+
+	v2s16 mhm_p = p2d * hm_split;
+	/*f32 corners[4] = {
+		m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
+		m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
+		m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
+		m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
+	};*/
+	
+	// Loop through sub-heightmaps
+	for(s16 y=0; y<hm_split; y++)
+	for(s16 x=0; x<hm_split; x++)
+	{
+		v2s16 p_in_sector = v2s16(x,y);
+		v2s16 mhm_p = p2d * hm_split + p_in_sector;
+		f32 corners[4] = {
+			m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
+			m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
+			m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
+			m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
+		};
+
+		/*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
+				<<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
+				<<std::endl;*/
+
+		FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
+				mhm_p, hm_d);
+		sector->setHeightmap(p_in_sector, hm);
+
+		//hm->generateContinued(1.0, 0.5, corners);
+		hm->generateContinued(0.5, 0.5, corners);
+
+		//hm->print();
+	}
+	
+	// Add dummy objects
+	core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
+	sector->setObjects(objects);
+	
+	/*
+		Insert to container
+	*/
+	m_sectors.insert(p2d, sector);
+	
+	return sector;
+}
+
+MapSector * ServerMap::emergeSector(v2s16 p2d)
+{
+	DSTACK("%s: p2d=(%d,%d)",
+			__FUNCTION_NAME,
+			p2d.X, p2d.Y);
+	
 	/*
 		Check chunk status
 	*/
@@ -2383,7 +3434,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 		chunk_exists = true;
 
 	/*
-		If chunk is not generated, generate chunk
+		If chunk is not fully generated, generate chunk
 	*/
 	if(chunk_exists == false)
 	{
@@ -2394,10 +3445,24 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 	/*
 		Return sector if it exists now
 	*/
-	sector = getSectorNoGenerateNoEx(p2d);
+	MapSector *sector = getSectorNoGenerateNoEx(p2d);
 	if(sector != NULL)
 		return sector;
 	
+	/*
+		Try to load it from disk
+	*/
+	if(loadSectorFull(p2d) == true)
+	{
+		MapSector *sector = getSectorNoGenerateNoEx(p2d);
+		if(sector == NULL)
+		{
+			dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
+			throw InvalidPositionException("");
+		}
+		return sector;
+	}
+
 	/*
 		generateChunk should have generated the sector
 	*/
@@ -3288,6 +4353,49 @@ MapBlock * ServerMap::generateBlock(
 	return block;
 }
 
+MapBlock * ServerMap::createBlock(v3s16 p)
+{
+	DSTACK("%s: p=(%d,%d,%d)",
+			__FUNCTION_NAME, p.X, p.Y, p.Z);
+	
+	v2s16 p2d(p.X, p.Z);
+	s16 block_y = p.Y;
+	/*
+		This will create or load a sector if not found in memory.
+		If block exists on disk, it will be loaded.
+
+		NOTE: On old save formats, this will be slow, as it generates
+		      lighting on blocks for them.
+	*/
+	ServerMapSector *sector;
+	try{
+		sector = (ServerMapSector*)createSector(p2d);
+		assert(sector->getId() == MAPSECTOR_SERVER);
+	}
+	/*catch(InvalidPositionException &e)
+	{
+		dstream<<"createBlock: createSector() failed"<<std::endl;
+		throw e;
+	}*/
+	catch(std::exception &e)
+	{
+		dstream<<"createBlock: createSector() failed: "
+				<<e.what()<<std::endl;
+		throw e;
+	}
+
+	/*
+		Try to get a block from the sector
+	*/
+
+	MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
+	if(block)
+		return block;
+	// Create blank
+	block = sector->createBlankBlock(block_y);
+	return block;
+}
+
 MapBlock * ServerMap::emergeBlock(
 		v3s16 p,
 		bool only_from_disk,
@@ -3299,10 +4407,6 @@ MapBlock * ServerMap::emergeBlock(
 			__FUNCTION_NAME,
 			p.X, p.Y, p.Z, only_from_disk);
 	
-	/*dstream<<"emergeBlock(): "
-			<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-			<<std::endl;*/
-			
 	v2s16 p2d(p.X, p.Z);
 	s16 block_y = p.Y;
 	/*
@@ -4434,7 +5538,6 @@ MapVoxelManipulator::~MapVoxelManipulator()
 			<<std::endl;*/
 }
 
-#if 1
 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 {
 	TimeTaker timer1("emerge", &emerge_time);
@@ -4492,51 +5595,11 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 			}
 		}
 
-		m_loaded_blocks.insert(p, true);
+		m_loaded_blocks.insert(p, !block_data_inexistent);
 	}
 
 	//dstream<<"emerge done"<<std::endl;
 }
-#endif
-
-#if 0
-/*
-	NOTE: This is slow
-*/
-void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
-{
-	TimeTaker timer1("emerge", &emerge_time);
-	
-	v3s16 size = a.getExtent();
-	
-	VoxelArea padded = a;
-	padded.pad(m_area.getExtent() / 4);
-	addArea(padded);
-
-	for(s16 z=0; z<size.Z; z++)
-	for(s16 y=0; y<size.Y; y++)
-	for(s16 x=0; x<size.X; x++)
-	{
-		v3s16 p(x,y,z);
-		s32 i = m_area.index(a.MinEdge + p);
-		// Don't touch nodes that have already been loaded
-		if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
-			continue;
-		try
-		{
-			TimeTaker timer1("emerge load", &emerge_load_time);
-			MapNode n = m_map->getNode(a.MinEdge + p);
-			m_data[i] = n;
-			m_flags[i] = 0;
-		}
-		catch(InvalidPositionException &e)
-		{
-			m_flags[i] = VOXELFLAG_INEXISTENT;
-		}
-	}
-}
-#endif
-
 
 /*
 	SUGG: Add an option to only update eg. water and air nodes.
@@ -4620,23 +5683,8 @@ ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
 
 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 {
-	// Just create the area to avoid segfaults
+	// Just create the area so that it can be pointed to
 	VoxelManipulator::emerge(a, caller_id);
-
-	/*
-		Just create the area to avoid segfaults
-	*/
-	/*addArea(a);
-	for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
-	for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
-	for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
-	{
-		s32 i = m_area.index(x,y,z);
-		// Don't touch nodes that have already been loaded
-		if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
-			continue;
-		m_flags[i] = VOXELFLAG_INEXISTENT;
-	}*/
 }
 
 void ManualMapVoxelManipulator::initialEmerge(
@@ -4681,6 +5729,9 @@ void ManualMapVoxelManipulator::initialEmerge(
 
 		if(block_data_inexistent)
 		{
+			/*
+				Mark area inexistent
+			*/
 			VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
 			// Fill with VOXELFLAG_INEXISTENT
 			for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
@@ -4691,7 +5742,41 @@ void ManualMapVoxelManipulator::initialEmerge(
 			}
 		}
 
-		m_loaded_blocks.insert(p, true);
+		m_loaded_blocks.insert(p, !block_data_inexistent);
+	}
+}
+
+void ManualMapVoxelManipulator::blitBackAll(
+		core::map<v3s16, MapBlock*> * modified_blocks)
+{
+	if(m_area.getExtent() == v3s16(0,0,0))
+		return;
+	
+	/*
+		Copy data of all blocks
+	*/
+	for(core::map<v3s16, bool>::Iterator
+			i = m_loaded_blocks.getIterator();
+			i.atEnd() == false; i++)
+	{
+		bool existed = i.getNode()->getValue();
+		if(existed == false)
+			continue;
+		v3s16 p = i.getNode()->getKey();
+		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+		if(block == NULL)
+		{
+			dstream<<"WARNING: "<<__FUNCTION_NAME
+					<<": got NULL block "
+					<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+					<<std::endl;
+			continue;
+		}
+
+		block->copyFrom(*this);
+
+		if(modified_blocks)
+			modified_blocks->insert(p, block);
 	}
 }
 
diff --git a/src/map.h b/src/map.h
index bc6984c88..757f06ab5 100644
--- a/src/map.h
+++ b/src/map.h
@@ -93,6 +93,8 @@ class Map : public NodeContainer, public Heightmappish
 	MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
 	// On failure throws InvalidPositionException
 	MapSector * getSectorNoGenerate(v2s16 p2d);
+	// Gets an existing sector or creates an empty one
+	//MapSector * getSectorCreate(v2s16 p2d);
 
 	/*
 		This is overloaded by ClientMap and ServerMap to allow
@@ -104,6 +106,8 @@ class Map : public NodeContainer, public Heightmappish
 	MapBlock * getBlockNoCreate(v3s16 p);
 	// Returns NULL if not found
 	MapBlock * getBlockNoCreateNoEx(v3s16 p);
+	// Gets an existing block or creates an empty one
+	//MapBlock * getBlockCreate(v3s16 p);
 	
 	// Returns InvalidPositionException if not found
 	f32 getGroundHeight(v2s16 p, bool generate=false);
@@ -382,12 +386,20 @@ class ServerMap : public Map
 		
 		This is mainly called by generateChunkRaw.
 	*/
-	ServerMapSector * generateSector(v2s16 p);
+	//ServerMapSector * generateSector(v2s16 p);
+	
+	/*
+		Get a sector from somewhere.
+		- Check memory
+		- Check disk (loads blocks also)
+		- Create blank one
+	*/
+	ServerMapSector * createSector(v2s16 p);
 
 	/*
 		Get a sector from somewhere.
 		- Check memory
-		- Check disk
+		- Check disk (loads blocks also)
 		- Generate chunk
 	*/
 	MapSector * emergeSector(v2s16 p);
@@ -399,6 +411,13 @@ class ServerMap : public Map
 			core::map<v3s16, MapBlock*> &changed_blocks,
 			core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
 	);
+	
+	/*
+		Get a block from somewhere.
+		- Memory
+		- Create blank
+	*/
+	MapBlock * createBlock(v3s16 p);
 
 	MapBlock * emergeBlock(
 			v3s16 p,
@@ -636,10 +655,8 @@ class MapVoxelManipulator : public VoxelManipulator
 protected:
 	Map *m_map;
 	/*
-		NOTE: This might be used or not
-		bool is dummy value
-		SUGG: How 'bout an another VoxelManipulator for storing the
-		      information about which block is loaded?
+		key = blockpos
+		value = block existed when loaded
 	*/
 	core::map<v3s16, bool> m_loaded_blocks;
 };
@@ -653,8 +670,12 @@ class ManualMapVoxelManipulator : public MapVoxelManipulator
 	virtual void emerge(VoxelArea a, s32 caller_id=-1);
 
 	void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
+	
+	// This is much faster with big chunks of generated data
+	void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks);
 
 protected:
+	bool m_create_area;
 };
 
 #endif
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index a7bc730ce..3e20cb4cf 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -1309,10 +1309,21 @@ void MapBlock::copyTo(VoxelManipulator &dst)
 	v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
 	VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
 	
+	// Copy from data to VoxelManipulator
 	dst.copyFrom(data, data_area, v3s16(0,0,0),
 			getPosRelative(), data_size);
 }
 
+void MapBlock::copyFrom(VoxelManipulator &dst)
+{
+	v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+	VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+	
+	// Copy from VoxelManipulator to data
+	dst.copyTo(data, data_area, v3s16(0,0,0),
+			getPosRelative(), data_size);
+}
+
 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
 {
 	/*
diff --git a/src/mapblock.h b/src/mapblock.h
index e39db35bd..02c072895 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -426,6 +426,8 @@ class MapBlock : public NodeContainer
 	
 	// Copies data to VoxelManipulator to getPosRelative()
 	void copyTo(VoxelManipulator &dst);
+	// Copies data from VoxelManipulator getPosRelative()
+	void copyFrom(VoxelManipulator &dst);
 
 	/*
 		MapBlockObject stuff
diff --git a/src/mapnode.h b/src/mapnode.h
index 07153f934..4385f9acf 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -438,6 +438,14 @@ struct MapNode
 		param2 = a_param2;
 	}
 
+	/*MapNode & operator=(const MapNode &other)
+	{
+		d = other.d;
+		param = other.param;
+		param2 = other.param2;
+		return *this;
+	}*/
+
 	bool operator==(const MapNode &other)
 	{
 		return (d == other.d
diff --git a/src/mapsector.cpp b/src/mapsector.cpp
index fa1bb68d0..1fd668793 100644
--- a/src/mapsector.cpp
+++ b/src/mapsector.cpp
@@ -570,10 +570,15 @@ ServerMapSector* ServerMapSector::deSerialize(
 
 	if(n != NULL)
 	{
-		dstream<<"deSerializing existent sectors not supported "
+		dstream<<"WARNING: deSerializing existent sectors not supported "
 				"at the moment, because code hasn't been tested."
 				<<std::endl;
-		assert(0);
+
+		//assert(0);
+		MapSector *sector = n->getValue();
+		assert(sector->getId() == MAPSECTOR_SERVER);
+		return (ServerMapSector*)sector;
+
 		// NOTE: At least hm_split mismatch would have to be checked
 		
 		//sector = n->getValue();
diff --git a/src/server.cpp b/src/server.cpp
index 80aa47671..28faaf440 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -606,6 +606,15 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 				{
 					block_is_invalid = true;
 				}
+				
+				v2s16 p2d(p.X, p.Z);
+				ServerMap *map = (ServerMap*)(&server->m_env.getMap());
+				v2s16 chunkpos = map->sector_to_chunk(p2d);
+				MapChunk *chunk = map->getChunk(chunkpos);
+				if(chunk == NULL)
+					block_is_invalid = true;
+				else if(chunk->getIsVolatile() == true)
+					block_is_invalid = true;
 			}
 
 			/*
@@ -3258,7 +3267,8 @@ Player *Server::emergePlayer(const char *name, const char *password,
 
 		player->setPosition(intToFloat(v3s16(
 				nodepos.X,
-				groundheight + 1,
+				//groundheight + 1,
+				groundheight + 15,
 				nodepos.Y
 		)));
 
diff --git a/src/utility.h b/src/utility.h
index a61de1c37..8839887f7 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -207,6 +207,10 @@ class SharedPtr
 	{
 		return ptr == t;
 	}
+	T & operator[](unsigned int i)
+	{
+		return ptr[i];
+	}
 private:
 	void drop()
 	{
@@ -572,6 +576,15 @@ inline bool isInArea(v2s16 p, s16 d)
 	);
 }
 
+inline bool isInArea(v3s16 p, v3s16 d)
+{
+	return (
+		p.X >= 0 && p.X < d.X &&
+		p.Y >= 0 && p.Y < d.Y &&
+		p.Z >= 0 && p.Z < d.Z
+	);
+}
+
 inline s16 rangelim(s16 i, s16 min, s16 max)
 {
 	if(i < min)
@@ -1459,6 +1472,13 @@ int myrand(void);
 void mysrand(unsigned seed);
 #define MYRAND_MAX 32767
 
+inline int myrand_range(int min, int max)
+{
+	if(min >= max)
+		return max;
+	return (myrand()%(max-min+1))+min;
+}
+
 /*
 	Some kind of a thing that stores attributes related to
 	coordinate points
diff --git a/src/voxel.cpp b/src/voxel.cpp
index 02635d3af..f60b641f1 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -41,7 +41,6 @@ VoxelManipulator::VoxelManipulator():
 	m_data(NULL),
 	m_flags(NULL)
 {
-	m_disable_water_climb = false;
 }
 
 VoxelManipulator::~VoxelManipulator()
@@ -221,6 +220,22 @@ void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
 	}
 }
 
+void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area,
+		v3s16 dst_pos, v3s16 from_pos, v3s16 size)
+{
+	for(s16 z=0; z<size.Z; z++)
+	for(s16 y=0; y<size.Y; y++)
+	{
+		s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z);
+		s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
+		memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode));
+	}
+}
+
+/*
+	Algorithms
+	-----------------------------------------------------
+*/
 
 void VoxelManipulator::clearFlag(u8 flags)
 {
@@ -541,7 +556,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
 	Lights neighbors of from_nodes, collects all them and then
 	goes on recursively.
 
-	NOTE: This is faster in small areas but will overflow the
+	NOTE: This is faster on small areas but will overflow the
 	      stack on large areas. Thus it is not used.
 */
 void VoxelManipulator::spreadLight(enum LightBank bank,
diff --git a/src/voxel.h b/src/voxel.h
index ff5d534cb..89333159c 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -262,7 +262,31 @@ class VoxelArea
 	{
 		return index(p.X, p.Y, p.Z);
 	}
+	
+	// Translate index in the X coordinate
+	void add_x(const v3s16 &extent, u32 &i, s16 a)
+	{
+		i += a;
+	}
+	// Translate index in the Y coordinate
+	void add_y(const v3s16 &extent, u32 &i, s16 a)
+	{
+		i += a * extent.X;
+	}
+	// Translate index in the Z coordinate
+	void add_z(const v3s16 &extent, u32 &i, s16 a)
+	{
+		i += a * extent.X*extent.Y;
+	}
+	// Translate index in space
+	void add_p(const v3s16 &extent, u32 &i, v3s16 a)
+	{
+		i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X;
+	}
 
+	/*
+		Print method for debugging
+	*/
 	void print(std::ostream &o) const
 	{
 		v3s16 e = getExtent();
@@ -394,6 +418,10 @@ class VoxelManipulator /*: public NodeContainer*/
 	*/
 	void copyFrom(MapNode *src, VoxelArea src_area,
 			v3s16 from_pos, v3s16 to_pos, v3s16 size);
+	
+	// Copy data
+	void copyTo(MapNode *dst, VoxelArea dst_area,
+			v3s16 dst_pos, v3s16 from_pos, v3s16 size);
 
 	/*
 		Algorithms
-- 
GitLab