diff --git a/src/constants.h b/src/constants.h
index f90b278d2b25ade145f26c0ce1744e1431a84ec2..c8c210b1307cd03c7076f569386aafc98ba17b1d 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -76,21 +76,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 // is very low
 #define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1
 
-// The fps limiter will leave this much free time
-//#define FREETIME_RATIO 0.15
-//#define FREETIME_RATIO 0.0
-#define FREETIME_RATIO 0.05
-
 #define PLAYER_INVENTORY_SIZE (8*4)
 
 #define SIGN_TEXT_MAX_LENGTH 50
 
 // Whether to catch all std::exceptions.
 // Assert will be called on such an event.
-#ifdef DEBUG
-	#define CATCH_UNHANDLED_EXCEPTIONS 0
-#else
+// In debug mode, leave these for the debugger and don't catch them.
+#ifdef NDEBUG
 	#define CATCH_UNHANDLED_EXCEPTIONS 1
+#else
+	#define CATCH_UNHANDLED_EXCEPTIONS 0
 #endif
 
 /*
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 4046b81b96b6b61acb55b918970e1fe906da6b9a..0665cd02eee12022058af3e6f7bc927a3b648ea8 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -49,8 +49,8 @@ void set_default_settings()
 	g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
 	//g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
 	g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
-	g_settings.setDefault("max_block_send_distance", "6");
-	g_settings.setDefault("max_block_generate_distance", "6");
+	g_settings.setDefault("max_block_send_distance", "7");
+	g_settings.setDefault("max_block_generate_distance", "7");
 	g_settings.setDefault("time_send_interval", "20");
 	g_settings.setDefault("time_speed", "96");
 	g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
diff --git a/src/main.cpp b/src/main.cpp
index 0dc822474ec9e61b0903564d9cbccfe8d09132d7..dc4716fabb35f9b58db33968f78286434145025d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -168,7 +168,7 @@ TODO: Make fetching sector's blocks more efficient when rendering
 
 TODO: Flowing water animation
 
-FIXME: The new texture stuff is slow on wine
+FIXME(FIXED): The new texture stuff is slow on wine
 	- A basic grassy ground block takes 20-40ms
 	- A bit more complicated block can take 270ms
 	  - On linux, a similar one doesn't take long at all (14ms)
@@ -182,6 +182,15 @@ FIXME: The new texture stuff is slow on wine
 	  is fast to compare, which refers to a cached string, or
 	* Make TextureSpec for using instead of strings
 
+FIXME(FIXED): A lock condition is possible:
+	1) MapBlock::updateMesh() is called from client asynchronously:
+	   - AsyncProcessData() -> Map::updateMeshes()
+	2) Asynchronous locks m_temp_mods_mutex
+	3) MapBlock::updateMesh() is called from client synchronously:
+	   - Client::step() -> Environment::step()
+	4) Synchronous starts waiting for m_temp_mods_mutex
+	5) Asynchronous calls getTexture, which starts waiting for main thread
+
 Configuration:
 --------------
 
@@ -255,6 +264,20 @@ Block object server side:
 NOTE: There are some lighting-related todos and fixmes in
       ServerMap::emergeBlock. And there always will be. 8)
 
+TODO: Mineral and ground material properties
+      - This way mineral ground toughness can be calculated with just
+	    some formula, as well as tool strengths
+
+TODO: Change AttributeList to split the area into smaller sections so
+      that searching won't be as heavy.
+
+TODO: Remove HMParams
+
+TODO: Flowing water to actually contain flow direction information
+
+TODO: Remove duplicate lighting implementation from Map (leave
+      VoxelManipulator, which is faster)
+
 FEATURE: Map generator version 2
 	- Create surface areas based on central points; a given point's
 	  area type is given by the nearest central point
@@ -269,6 +292,11 @@ FEATURE: Map generator version 2
 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?
+		- It has to be split vertically also
+		- 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
       * Make the stone level with a heightmap
 	  * Carve out stuff in the stone
 	  * Dump dirt all around, and simulate it falling off steep
@@ -283,20 +311,13 @@ 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: Mineral and ground material properties
-      - This way mineral ground toughness can be calculated with just
-	    some formula, as well as tool strengths
-
-TODO: Change AttributeList to split the area into smaller sections so
-      that searching won't be as heavy.
-
-TODO: Remove HMParams
-
-TODO: Flowing water to actually contain flow direction information
-
-TODO: Remove duplicate lighting implementation from Map (leave
-      VoxelManipulator, which is faster)
+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:
 ----------
@@ -1522,6 +1543,16 @@ int main(int argc, char *argv[])
 	srand(time(0));
 	mysrand(time(0));
 
+	/*
+		Pre-initialize some stuff with a dummy irrlicht wrapper.
+
+		These are needed for unit tests at least.
+	*/
+	
+	IIrrlichtWrapper irrlicht_dummy;
+
+	init_mapnode(&irrlicht_dummy);
+
 	/*
 		Run unit tests
 	*/
@@ -1684,7 +1715,7 @@ int main(int argc, char *argv[])
 	skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
 	
 	/*
-		Preload some textures
+		Preload some textures and stuff
 	*/
 
 	init_content_inventory_texture_paths();
@@ -2131,20 +2162,6 @@ int main(int argc, char *argv[])
 				dtime_jitter1_max_fraction
 						= dtime_jitter1_max_sample / (dtime_avg1+0.001);
 				jitter1_max = 0.0;
-				
-				/*
-					Control freetime ratio
-				*/
-				/*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
-				{
-					if(g_freetime_ratio < FREETIME_RATIO_MAX)
-						g_freetime_ratio += 0.01;
-				}
-				else
-				{
-					if(g_freetime_ratio > FREETIME_RATIO_MIN)
-						g_freetime_ratio -= 0.01;
-				}*/
 			}
 		}
 		
diff --git a/src/map.cpp b/src/map.cpp
index 2782cef03dd1a210118ee43850f07fd337ed163b..cc1a6d638d4c07c88c4472cabcb86a2d5fc32c08 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -67,10 +67,8 @@ Map::~Map()
 	}
 }
 
-MapSector * Map::getSectorNoGenerate(v2s16 p)
+MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
 {
-	JMutexAutoLock lock(m_sector_mutex);
-
 	if(m_sector_cache != NULL && p == m_sector_cache_p){
 		MapSector * sector = m_sector_cache;
 		// Reset inactivity timer
@@ -79,11 +77,9 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
 	}
 	
 	core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
-	// If sector doesn't exist, throw an exception
+	
 	if(n == NULL)
-	{
-		throw InvalidPositionException();
-	}
+		return NULL;
 	
 	MapSector *sector = n->getValue();
 	
@@ -91,13 +87,27 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
 	m_sector_cache_p = p;
 	m_sector_cache = sector;
 
-	//MapSector * ref(sector);
-	
 	// Reset inactivity timer
 	sector->usage_timer = 0.0;
 	return sector;
 }
 
+MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
+{
+	JMutexAutoLock lock(m_sector_mutex);
+
+	return getSectorNoGenerateNoExNoLock(p);
+}
+
+MapSector * Map::getSectorNoGenerate(v2s16 p)
+{
+	MapSector *sector = getSectorNoGenerateNoEx(p);
+	if(sector == NULL)
+		throw InvalidPositionException();
+	
+	return sector;
+}
+
 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
 {	
 	v2s16 p2d(p3d.X, p3d.Z);
@@ -630,6 +640,8 @@ void Map::updateLighting(enum LightBank bank,
 	// For debugging
 	//bool debug=true;
 	//u32 count_was = modified_blocks.size();
+	
+	core::map<v3s16, MapBlock*> blocks_to_update;
 
 	core::map<v3s16, bool> light_sources;
 	
@@ -650,6 +662,8 @@ void Map::updateLighting(enum LightBank bank,
 			v3s16 pos = block->getPos();
 			modified_blocks.insert(pos, block);
 
+			blocks_to_update.insert(pos, block);
+
 			/*
 				Clear all light from block
 			*/
@@ -699,10 +713,12 @@ void Map::updateLighting(enum LightBank bank,
 			}
 			else if(bank == LIGHTBANK_NIGHT)
 			{
+				// For night lighting, sunlight is not propagated
 				break;
 			}
 			else
 			{
+				// Invalid lighting bank
 				assert(0);
 			}
 				
@@ -710,7 +726,7 @@ void Map::updateLighting(enum LightBank bank,
 					<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
 					<<std::endl;*/
 
-			// Else get the block below and loop to it
+			// Bottom sunlight is not valid; get the block and loop to it
 
 			pos.Y--;
 			try{
@@ -737,13 +753,6 @@ void Map::updateLighting(enum LightBank bank,
 		dstream<<"unspreadLight modified "<<diff<<std::endl;
 	}
 
-	// TODO: Spread light from propagated sunlight?
-	// Yes, add it to light_sources... somehow.
-	// It has to be added at somewhere above, in the loop.
-	// TODO
-	// NOTE: This actually works fine without doing so
-	//       - Find out why it works
-
 	{
 		TimeTaker timer("spreadLight");
 		spreadLight(bank, light_sources, modified_blocks);
@@ -759,17 +768,44 @@ void Map::updateLighting(enum LightBank bank,
 	
 	{
 		//MapVoxelManipulator vmanip(this);
-
-		ManualMapVoxelManipulator vmanip(this);
 		
+		// Make a manual voxel manipulator and load all the blocks
+		// that touch the requested blocks
+		ManualMapVoxelManipulator vmanip(this);
 		core::map<v3s16, MapBlock*>::Iterator i;
-		i = a_blocks.getIterator();
+		i = blocks_to_update.getIterator();
 		for(; i.atEnd() == false; i++)
 		{
 			MapBlock *block = i.getNode()->getValue();
 			v3s16 p = block->getPos();
+			
+			// Add all surrounding blocks
 			vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
+
+			/*
+				Add all surrounding blocks that have up-to-date lighting
+				NOTE: This doesn't quite do the job (not everything
+				      appropriate is lighted)
+			*/
+			/*for(s16 z=-1; z<=1; z++)
+			for(s16 y=-1; y<=1; y++)
+			for(s16 x=-1; x<=1; x++)
+			{
+				v3s16 p(x,y,z);
+				MapBlock *block = getBlockNoCreateNoEx(p);
+				if(block == NULL)
+					continue;
+				if(block->isDummy())
+					continue;
+				if(block->getLightingExpired())
+					continue;
+				vmanip.initialEmerge(p, p);
+			}*/
+			
+			// Lighting of block will be updated completely
+			block->setLightingExpired(false);
 		}
+
 		{
 			//TimeTaker timer("unSpreadLight");
 			vmanip.unspreadLight(bank, unlight_from, light_sources);
@@ -1407,6 +1443,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 	u32 loopcount = 0;
 	u32 initial_size = m_transforming_liquid.size();
 
+	//dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
+
 	while(m_transforming_liquid.size() != 0)
 	{
 		/*
@@ -1682,6 +1720,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 	Map(dout_server),
 	m_heightmap(NULL)
 {
+	
+	//m_chunksize = 64;
+	//m_chunksize = 16;
+	//m_chunksize = 8;
+	m_chunksize = 2;
+
 	/*
 		Experimental and debug stuff
 	*/
@@ -1862,7 +1906,7 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 		list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
 		list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
 	}
-
+	
 	/*
 		Try to load map; if not found, create a new one.
 	*/
@@ -1917,18 +1961,6 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
 	dstream<<DTIME<<"Initializing new map."<<std::endl;
 	
 	// Create master heightmap
-	/*ValueGenerator *maxgen =
-			ValueGenerator::deSerialize(hmp.randmax);
-	ValueGenerator *factorgen =
-			ValueGenerator::deSerialize(hmp.randfactor);
-	ValueGenerator *basegen =
-			ValueGenerator::deSerialize(hmp.base);
-	m_heightmap = new UnlimitedHeightmap
-			(hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
-
-	/*m_heightmap = new UnlimitedHeightmap
-			(hmp.blocksize, &m_padb);*/
-
 	m_heightmap = new UnlimitedHeightmap
 			(32, &m_padb);
 	
@@ -1966,29 +1998,134 @@ ServerMap::~ServerMap()
 	
 	if(m_heightmap != NULL)
 		delete m_heightmap;
+	
+	/*
+		Free all MapChunks
+	*/
+	core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
+	for(; i.atEnd() == false; i++)
+	{
+		MapChunk *chunk = i.getNode()->getValue();
+		delete chunk;
+	}
 }
 
-MapSector * ServerMap::emergeSector(v2s16 p2d)
+MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
 {
-	DSTACK("%s: p2d=(%d,%d)",
-			__FUNCTION_NAME,
-			p2d.X, p2d.Y);
-	// Check that it doesn't exist already
-	try{
-		return getSectorNoGenerate(p2d);
+	// Return if chunk already exists
+	MapChunk *chunk = getChunk(chunkpos);
+	if(chunk)
+		return chunk;
+	
+	/*
+		Add all sectors
+	*/
+
+	dstream<<"generateChunkRaw(): "
+			<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
+			<<std::endl;
+	
+	TimeTaker timer("generateChunkRaw()");
+
+	v2s16 sectorpos_base = chunk_to_sector(chunkpos);
+
+	core::map<v3s16, MapBlock*> changed_blocks;
+	core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
+
+	u32 generated_block_count = 0;
+
+	for(s16 y=0; y<m_chunksize; y++)
+	{
+		/*dstream<<"Generating sectors "
+				<<"("<<sectorpos_base.X<<"..."
+				<<(sectorpos_base.X+m_chunksize-1)
+				<<", "<<y<<")"
+				<<std::endl;*/
+		
+		// With caves_amount attribute fetch: ~90ms (379ms peaks)
+		// Without: ~38ms (396ms peaks)
+		//TimeTaker timer("Chunk sector row");
+
+		for(s16 x=0; x<m_chunksize; x++)
+		{
+			v2s16 sectorpos = sectorpos_base + v2s16(x,y);
+
+			/*dstream<<"Generating sector "
+					<<"("<<sectorpos.X<<","<<sectorpos.Y<<")"
+					<<std::endl;*/
+
+			// 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++;
+			}
+		}
 	}
-	catch(InvalidPositionException &e)
+
+	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)
+{
 	/*
-		Try to load the sector from disk.
+		Generate chunk and neighbors
 	*/
-	if(loadSectorFull(p2d) == true)
+	for(s16 x=-1; x<=1; x++)
+	for(s16 y=-1; y<=1; y++)
 	{
-		return getSectorNoGenerate(p2d);
+		generateChunkRaw(chunkpos + v2s16(x,y));
 	}
 
+	/*
+		Get chunk
+	*/
+	MapChunk *chunk = getChunk(chunkpos);
+	assert(chunk);
+	// Set non-volatile
+	chunk->setIsVolatile(false);
+	// Return it
+	return chunk;
+}
+
+ServerMapSector * ServerMap::generateSector(v2s16 p2d)
+{
+	DSTACK("%s: p2d=(%d,%d)",
+			__FUNCTION_NAME,
+			p2d.X, p2d.Y);
+	
+	// Check that it doesn't exist already
+	ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
+	if(sector != NULL)
+		return sector;
+	
 	/*
 		If there is no master heightmap, throw.
 	*/
@@ -2016,7 +2153,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 	// Heightmap side width
 	s16 hm_d = MAP_BLOCKSIZE / hm_split;
 
-	ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
+	sector = new ServerMapSector(this, p2d, hm_split);
 	
 	// Sector position on map in nodes
 	v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
@@ -2068,7 +2205,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 		Get local attributes
 	*/
 	
-	float local_plants_amount = 0.0;
+	float local_plants_amount = 0.5;
+	
+#if 0
 	{
 		//dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
 		//TimeTaker attrtimer("emergeSector() attribute fetch");
@@ -2081,6 +2220,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 		local_plants_amount =
 				palist->getInterpolatedFloat(nodepos2d);
 	}
+#endif
 
 	/*
 		Generate sector heightmap
@@ -2201,91 +2341,104 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
 	/*
 		Insert to container
 	*/
-	JMutexAutoLock lock(m_sector_mutex);
 	m_sectors.insert(p2d, sector);
 	
 	return sector;
 }
 
-MapBlock * ServerMap::emergeBlock(
-		v3s16 p,
-		bool only_from_disk,
-		core::map<v3s16, MapBlock*> &changed_blocks,
-		core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
-)
+MapSector * ServerMap::emergeSector(v2s16 p2d)
 {
-	DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
+	DSTACK("%s: p2d=(%d,%d)",
 			__FUNCTION_NAME,
-			p.X, p.Y, p.Z, only_from_disk);
-			
-	/*dstream<<"ServerMap::emergeBlock(): "
-			<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-			<<", only_from_disk="<<only_from_disk<<std::endl;*/
-	v2s16 p2d(p.X, p.Z);
-	s16 block_y = p.Y;
+			p2d.X, p2d.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.
+		Check if it exists already in memory
 	*/
-	ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
-	assert(sector->getId() == MAPSECTOR_SERVER);
-
-	// Try to get a block from the sector
-	MapBlock *block = NULL;
-	bool not_on_disk = false;
-	try{
-		block = sector->getBlockNoCreate(block_y);
-		if(block->isDummy() == true)
-			not_on_disk = true;
-		else
-			return block;
-	}
-	catch(InvalidPositionException &e)
-	{
-		not_on_disk = true;
-	}
+	MapSector *sector = getSectorNoGenerateNoEx(p2d);
+	if(sector != NULL)
+		return sector;
 	
 	/*
-		If block was not found on disk and not going to generate a
-		new one, make sure there is a dummy block in place.
+		Try to load it from disk
 	*/
-	if(not_on_disk && only_from_disk)
+	if(loadSectorFull(p2d) == true)
 	{
-		if(block == NULL)
+		MapSector *sector = getSectorNoGenerateNoEx(p2d);
+		if(sector == NULL)
 		{
-			// Create dummy block
-			block = new MapBlock(this, p, true);
-
-			// Add block to sector
-			sector->insertBlock(block);
+			dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
+			throw InvalidPositionException("");
 		}
-		// Done.
-		return block;
+		return sector;
 	}
 
-	//dstream<<"Not found on disk, generating."<<std::endl;
-	// 0ms
-	//TimeTaker("emergeBlock() generate");
-
 	/*
-		Do not generate over-limit
+		Check chunk status
 	*/
-	if(blockpos_over_limit(p))
-		throw InvalidPositionException("emergeBlock(): pos. over limit");
+	v2s16 chunkpos = sector_to_chunk(p2d);
+	bool chunk_exists = false;
+	MapChunk *chunk = getChunk(chunkpos);
+	if(chunk && chunk->getIsVolatile() == false)
+		chunk_exists = true;
 
 	/*
-		OK; Not found.
-
-		Go on generating the block.
+		If chunk is not generated, generate chunk
+	*/
+	if(chunk_exists == false)
+	{
+		// Generate chunk and neighbors
+		generateChunk(chunkpos);
+	}
+	
+	/*
+		Return sector if it exists now
+	*/
+	sector = getSectorNoGenerateNoEx(p2d);
+	if(sector != NULL)
+		return sector;
+	
+	/*
+		generateChunk should have generated the sector
+	*/
+	assert(0);
 
-		TODO: If a dungeon gets generated so that it's side gets
-		      revealed to the outside air, the lighting should be
-			  recalculated.
+	/*
+		Generate directly
 	*/
+	//return generateSector();
+}
+
+MapBlock * ServerMap::generateBlock(
+		v3s16 p,
+		MapBlock *original_dummy,
+		ServerMapSector *sector,
+		core::map<v3s16, MapBlock*> &changed_blocks,
+		core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+)
+{
+	DSTACK("%s: p=(%d,%d,%d)",
+			__FUNCTION_NAME,
+			p.X, p.Y, p.Z);
+	
+	/*dstream<<"generateBlock(): "
+			<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+			<<std::endl;*/
+	
+	MapBlock *block = original_dummy;
+			
+	v2s16 p2d(p.X, p.Z);
+	s16 block_y = p.Y;
 	
+	/*
+		Do not generate over-limit
+	*/
+	if(blockpos_over_limit(p))
+	{
+		dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
+		throw InvalidPositionException("generateBlock(): pos. over limit");
+	}
+
 	/*
 		If block doesn't exist, create one.
 		If it exists, it is a dummy. In that case unDummify() it.
@@ -2318,7 +2471,7 @@ MapBlock * ServerMap::emergeBlock(
 	for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
 	for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
 	{
-		//dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
+		//dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
 
 		float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
 		//assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
@@ -2451,23 +2604,25 @@ MapBlock * ServerMap::emergeBlock(
 		Get local attributes
 	*/
 
-	//dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
+	//dstream<<"generateBlock(): Getting local attributes"<<std::endl;
 
-	float caves_amount = 0;
-	
+	float caves_amount = 0.5;
+
+#if 0
 	{
 		/*
 			NOTE: BEWARE: Too big amount of attribute points slows verything
 			down by a lot.
 			1 interpolation from 5000 points takes 2-3ms.
 		*/
-		//TimeTaker timer("emergeBlock() local attribute retrieval");
+		//TimeTaker timer("generateBlock() local attribute retrieval");
 		v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
 		PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
 		caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
 	}
+#endif
 
-	//dstream<<"emergeBlock(): Done"<<std::endl;
+	//dstream<<"generateBlock(): Done"<<std::endl;
 
 	/*
 		Generate dungeons
@@ -2617,10 +2772,10 @@ MapBlock * ServerMap::emergeBlock(
 			do_generate_dungeons = false;
 		}
 		// Don't generate if mostly underwater surface
-		else if(mostly_underwater_surface)
+		/*else if(mostly_underwater_surface)
 		{
 			do_generate_dungeons = false;
-		}
+		}*/
 		// Partly underground = cave
 		else if(!completely_underground)
 		{
@@ -2723,7 +2878,7 @@ MapBlock * ServerMap::emergeBlock(
 	
 	/*
 		This is used for guessing whether or not the block should
-		receive sunlight from the top if the top block doesn't exist
+		receive sunlight from the top if the block above doesn't exist
 	*/
 	block->setIsUnderground(completely_underground);
 
@@ -3075,7 +3230,7 @@ MapBlock * ServerMap::emergeBlock(
 		}
 		else
 		{
-			dstream<<"ServerMap::emergeBlock(): "
+			dstream<<"ServerMap::generateBlock(): "
 					"Invalid heightmap object"
 					<<std::endl;
 		}
@@ -3098,6 +3253,148 @@ MapBlock * ServerMap::emergeBlock(
 	{
 		objects->remove(*i);
 	}
+	
+	/*
+		Translate sector's changed blocks to global changed blocks
+	*/
+	
+	for(core::map<s16, MapBlock*>::Iterator
+			i = changed_blocks_sector.getIterator();
+			i.atEnd() == false; i++)
+	{
+		MapBlock *block = i.getNode()->getValue();
+
+		changed_blocks.insert(block->getPos(), block);
+	}
+
+	block->setLightingExpired(true);
+	
+#if 0
+	/*
+		Debug information
+	*/
+	dstream
+	<<"lighting_invalidated_blocks.size()"
+	<<", has_dungeons"
+	<<", completely_ug"
+	<<", some_part_ug"
+	<<"  "<<lighting_invalidated_blocks.size()
+	<<", "<<has_dungeons
+	<<", "<<completely_underground
+	<<", "<<some_part_underground
+	<<std::endl;
+#endif
+
+	return block;
+}
+
+MapBlock * ServerMap::emergeBlock(
+		v3s16 p,
+		bool only_from_disk,
+		core::map<v3s16, MapBlock*> &changed_blocks,
+		core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+)
+{
+	DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
+			__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;
+	/*
+		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*)emergeSector(p2d);
+		assert(sector->getId() == MAPSECTOR_SERVER);
+	}
+	/*catch(InvalidPositionException &e)
+	{
+		dstream<<"emergeBlock: emergeSector() failed"<<std::endl;
+		throw e;
+	}*/
+	catch(std::exception &e)
+	{
+		dstream<<"emergeBlock: emergeSector() failed: "
+				<<e.what()<<std::endl;
+		throw e;
+	}
+
+	/*
+		Try to get a block from the sector
+	*/
+
+	bool does_not_exist = false;
+	bool lighting_expired = false;
+	MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
+
+	if(block == NULL)
+	{
+		does_not_exist = true;
+	}
+	else if(block->isDummy() == true)
+	{
+		does_not_exist = true;
+	}
+	else if(block->getLightingExpired())
+	{
+		lighting_expired = true;
+	}
+	else
+	{
+		// Valid block
+		//dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
+		return block;
+	}
+	
+	/*
+		If block was not found on disk and not going to generate a
+		new one, make sure there is a dummy block in place.
+	*/
+	if(only_from_disk && (does_not_exist || lighting_expired))
+	{
+		//dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
+
+		if(block == NULL)
+		{
+			// Create dummy block
+			block = new MapBlock(this, p, true);
+
+			// Add block to sector
+			sector->insertBlock(block);
+		}
+		// Done.
+		return block;
+	}
+
+	//dstream<<"Not found on disk, generating."<<std::endl;
+	// 0ms
+	//TimeTaker("emergeBlock() generate");
+
+	//dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
+
+	/*
+		If the block doesn't exist, generate the block.
+	*/
+	if(does_not_exist)
+	{
+		block = generateBlock(p, block, sector, changed_blocks,
+				lighting_invalidated_blocks); 
+	}
+
+	if(lighting_expired)
+	{
+		lighting_invalidated_blocks.insert(p, block);
+	}
 
 	/*
 		Initially update sunlight
@@ -3112,7 +3409,8 @@ MapBlock * ServerMap::emergeBlock(
 
 		// If sunlight didn't reach everywhere and part of block is
 		// above ground, lighting has to be properly updated
-		if(black_air_left && some_part_underground)
+		//if(black_air_left && some_part_underground)
+		if(black_air_left)
 		{
 			lighting_invalidated_blocks[block->getPos()] = block;
 		}
@@ -3122,37 +3420,7 @@ MapBlock * ServerMap::emergeBlock(
 			lighting_invalidated_blocks[block->getPos()] = block;
 		}
 	}
-
-	/*
-		Translate sector's changed blocks to global changed blocks
-	*/
 	
-	for(core::map<s16, MapBlock*>::Iterator
-			i = changed_blocks_sector.getIterator();
-			i.atEnd() == false; i++)
-	{
-		MapBlock *block = i.getNode()->getValue();
-
-		changed_blocks.insert(block->getPos(), block);
-	}
-
-	/*
-		Debug information
-	*/
-	if(0)
-	{
-		dstream
-		<<"lighting_invalidated_blocks.size()"
-		<<", has_dungeons"
-		<<", completely_ug"
-		<<", some_part_ug"
-		<<"  "<<lighting_invalidated_blocks.size()
-		<<", "<<has_dungeons
-		<<", "<<completely_underground
-		<<", "<<some_part_underground
-		<<std::endl;
-	}
-
 	/*
 		Debug mode operation
 	*/
diff --git a/src/map.h b/src/map.h
index 9140d4bf254372b50af39e9224ff101fe1714a27..bc6984c8851e7400c9b356dd1a8e200e8366241d 100644
--- a/src/map.h
+++ b/src/map.h
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapsector.h"
 #include "constants.h"
 #include "voxel.h"
+#include "mapchunk.h"
 
 #define MAPTYPE_BASE 0
 #define MAPTYPE_SERVER 1
@@ -85,9 +86,14 @@ class Map : public NodeContainer, public Heightmappish
 			(float)p.Z * BS + 0.5*BS
 		);
 	}
-
-	//bool sectorExists(v2s16 p);
+	
+	// On failure returns NULL
+	MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
+	// On failure returns NULL
+	MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
+	// On failure throws InvalidPositionException
 	MapSector * getSectorNoGenerate(v2s16 p2d);
+
 	/*
 		This is overloaded by ClientMap and ServerMap to allow
 		their differing fetch methods.
@@ -318,9 +324,90 @@ class ServerMap : public Map
 	}
 
 	/*
-		Forcefully get a sector from somewhere
+		Map generation
+	*/
+	
+	// Returns the position of the chunk where the sector is in
+	v2s16 sector_to_chunk(v2s16 sectorpos)
+	{
+		sectorpos.X += m_chunksize / 2;
+		sectorpos.Y += m_chunksize / 2;
+		v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
+		return chunkpos;
+	}
+	
+	// Returns the position of the (0,0) sector of the chunk
+	v2s16 chunk_to_sector(v2s16 chunkpos)
+	{
+		v2s16 sectorpos(
+			chunkpos.X * m_chunksize,
+			chunkpos.Y * m_chunksize
+		);
+		sectorpos.X -= m_chunksize / 2;
+		sectorpos.Y -= m_chunksize / 2;
+		return sectorpos;
+	}
+
+	/*
+		Get a chunk.
+	*/
+	MapChunk *getChunk(v2s16 chunkpos)
+	{
+		core::map<v2s16, MapChunk*>::Node *n;
+		n = m_chunks.find(chunkpos);
+		if(n == NULL)
+			return NULL;
+		return n->getValue();
+	}
+
+	/*
+		Generate a chunk.
+
+		All chunks touching this one can be altered also.
+
+		Doesn't update lighting.
+	*/
+	MapChunk* generateChunkRaw(v2s16 chunkpos);
+	
+	/*
+		Generate a chunk and its neighbors so that it won't be touched
+		anymore.
+
+		Doesn't update lighting.
+	*/
+	MapChunk* generateChunk(v2s16 chunkpos);
+	
+	/*
+		Generate a sector.
+		
+		This is mainly called by generateChunkRaw.
+	*/
+	ServerMapSector * generateSector(v2s16 p);
+
+	/*
+		Get a sector from somewhere.
+		- Check memory
+		- Check disk
+		- Generate chunk
 	*/
 	MapSector * emergeSector(v2s16 p);
+
+	MapBlock * generateBlock(
+			v3s16 p,
+			MapBlock *original_dummy,
+			ServerMapSector *sector,
+			core::map<v3s16, MapBlock*> &changed_blocks,
+			core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+	);
+
+	MapBlock * emergeBlock(
+			v3s16 p,
+			bool only_from_disk,
+			core::map<v3s16, MapBlock*> &changed_blocks,
+			core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+	);
+
+#if 0
 	/*
 		Forcefully get a block from somewhere.
 
@@ -346,7 +433,12 @@ class ServerMap : public Map
 			core::map<v3s16, MapBlock*> &changed_blocks,
 			core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
 	);
+#endif
 
+	/*
+		Misc. helper functions for fiddling with directory and file
+		names when saving
+	*/
 	void createDir(std::string path);
 	void createSaveDir();
 	// returns something like "xxxxxxxx"
@@ -396,8 +488,17 @@ class ServerMap : public Map
 
 	std::string m_savedir;
 	bool m_map_saving_enabled;
+
+	// Chunk size in MapSectors
+	s16 m_chunksize;
+	// Chunks
+	core::map<v2s16, MapChunk*> m_chunks;
 };
 
+/*
+	ClientMap stuff
+*/
+
 #ifndef SERVER
 
 struct MapDrawControl
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index b346b098090ffd5edd055ae8e8a662c4ad017752..a7bc730ce076723ce69062fa39c7a6f0ef0210b5 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -293,7 +293,8 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
 	Gets node tile from any place relative to block.
 	Returns TILE_NODE if doesn't exist or should not be drawn.
 */
-TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
+TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+		NodeModMap &temp_mods)
 {
 	TileSpec spec;
 	spec = mn.getTile(face_dir);
@@ -301,13 +302,15 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
 	/*
 		Check temporary modifications on this node
 	*/
-	core::map<v3s16, NodeMod>::Node *n;
+	/*core::map<v3s16, NodeMod>::Node *n;
 	n = m_temp_mods.find(p);
-
 	// If modified
 	if(n != NULL)
 	{
-		struct NodeMod mod = n->getValue();
+		struct NodeMod mod = n->getValue();*/
+	NodeMod mod;
+	if(temp_mods.get(p, &mod))
+	{
 		if(mod.type == NODEMOD_CHANGECONTENT)
 		{
 			MapNode mn2(mod.param);
@@ -326,18 +329,20 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
 	return spec;
 }
 
-u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
+u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
 {
 	/*
 		Check temporary modifications on this node
 	*/
-	core::map<v3s16, NodeMod>::Node *n;
+	/*core::map<v3s16, NodeMod>::Node *n;
 	n = m_temp_mods.find(p);
-
 	// If modified
 	if(n != NULL)
 	{
-		struct NodeMod mod = n->getValue();
+		struct NodeMod mod = n->getValue();*/
+	NodeMod mod;
+	if(temp_mods.get(p, &mod))
+	{
 		if(mod.type == NODEMOD_CHANGECONTENT)
 		{
 			// Overrides content
@@ -376,7 +381,8 @@ void MapBlock::updateFastFaceRow(
 		v3f translate_dir_f,
 		v3s16 face_dir,
 		v3f face_dir_f,
-		core::array<FastFace> &dest)
+		core::array<FastFace> &dest,
+		NodeModMap &temp_mods)
 {
 	v3s16 p = startpos;
 	
@@ -387,8 +393,8 @@ void MapBlock::updateFastFaceRow(
 
 	u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
 		
-	TileSpec tile0 = getNodeTile(n0, p, face_dir);
-	TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
+	TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
+	TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
 
 	for(u16 j=0; j<length; j++)
 	{
@@ -406,8 +412,8 @@ void MapBlock::updateFastFaceRow(
 			p_next = p + translate_dir;
 			n0_next = getNodeParentNoEx(p_next);
 			n1_next = getNodeParentNoEx(p_next + face_dir);
-			tile0_next = getNodeTile(n0_next, p_next, face_dir);
-			tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
+			tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
+			tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
 			light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
 
 			if(tile0_next == tile0
@@ -427,8 +433,8 @@ void MapBlock::updateFastFaceRow(
 			*/
 			//u8 mf = face_contents(tile0, tile1);
 			// This is hackish
-			u8 content0 = getNodeContent(p, n0);
-			u8 content1 = getNodeContent(p + face_dir, n1);
+			u8 content0 = getNodeContent(p, n0, temp_mods);
+			u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
 			u8 mf = face_contents(content0, content1);
 			
 			if(mf != 0)
@@ -594,6 +600,16 @@ void MapBlock::updateMesh(u32 daynight_ratio)
 	v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
 			getPosRelative().Z); // floating point conversion
 	
+	
+	/*
+		Avoid interlocks by copying m_temp_mods
+	*/
+	NodeModMap temp_mods;
+	{
+		JMutexAutoLock lock(m_temp_mods_mutex);
+		m_temp_mods.copy(temp_mods);
+	}
+
 	/*
 		We are including the faces of the trailing edges of the block.
 		This means that when something changes, the caller must
@@ -606,9 +622,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
 		// 4-23ms for MAP_BLOCKSIZE=16
 		//TimeTaker timer2("updateMesh() collect");
 
-		// Lock this, as m_temp_mods will be used directly
-		JMutexAutoLock lock(m_temp_mods_mutex);
-
 		/*
 			Go through every y,z and get top faces in rows of x+
 		*/
@@ -620,7 +633,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
 						v3f  (1,0,0),
 						v3s16(0,1,0), //face dir
 						v3f  (0,1,0),
-						fastfaces_new);
+						fastfaces_new,
+						temp_mods);
 			}
 		}
 		/*
@@ -634,7 +648,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
 						v3f  (0,0,1),
 						v3s16(1,0,0),
 						v3f  (1,0,0),
-						fastfaces_new);
+						fastfaces_new,
+						temp_mods);
 			}
 		}
 		/*
@@ -648,7 +663,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
 						v3f  (1,0,0),
 						v3s16(0,0,1),
 						v3f  (0,0,1),
-						fastfaces_new);
+						fastfaces_new,
+						temp_mods);
 			}
 		}
 	}
@@ -1297,10 +1313,6 @@ void MapBlock::copyTo(VoxelManipulator &dst)
 			getPosRelative(), data_size);
 }
 
-/*void getPseudoObjects(v3f origin, f32 max_d,
-		core::array<DistanceSortedObject> &dest)
-{
-}*/
 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
 {
 	/*
@@ -1501,6 +1513,8 @@ void MapBlock::serialize(std::ostream &os, u8 version)
 			flags |= 1;
 		if(m_day_night_differs)
 			flags |= 2;
+		if(m_lighting_expired)
+			flags |= 3;
 		os.write((char*)&flags, 1);
 
 		u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
@@ -1622,6 +1636,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
 		is.read((char*)&flags, 1);
 		is_underground = (flags & 1) ? true : false;
 		m_day_night_differs = (flags & 2) ? true : false;
+		m_lighting_expired = (flags & 3) ? true : false;
 
 		// Uncompress data
 		std::ostringstream os(std::ios_base::binary);
diff --git a/src/mapblock.h b/src/mapblock.h
index dd527766861c6ae7e128e847c366bca5b10054cd..e39db35bdb8dde49caa68134c3a47f199f3347c3 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -70,6 +70,74 @@ struct NodeMod
 	u16 param;
 };
 
+class NodeModMap
+{
+public:
+	/*
+		returns true if the mod was different last time
+	*/
+	bool set(v3s16 p, const NodeMod &mod)
+	{
+		// See if old is different, cancel if it is not different.
+		core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
+		if(n)
+		{
+			NodeMod old = n->getValue();
+			if(old == mod)
+				return false;
+
+			n->setValue(mod);
+		}
+		else
+		{
+			m_mods.insert(p, mod);
+		}
+		
+		return true;
+	}
+	// Returns true if there was one
+	bool get(v3s16 p, NodeMod *mod)
+	{
+		core::map<v3s16, NodeMod>::Node *n;
+		n = m_mods.find(p);
+		if(n == NULL)
+			return false;
+		if(mod)
+			*mod = n->getValue();
+		return true;
+	}
+	bool clear(v3s16 p)
+	{
+		if(m_mods.find(p))
+		{
+			m_mods.remove(p);
+			return true;
+		}
+		return false;
+	}
+	bool clear()
+	{
+		if(m_mods.size() == 0)
+			return false;
+		m_mods.clear();
+		return true;
+	}
+	void copy(NodeModMap &dest)
+	{
+		dest.m_mods.clear();
+
+		for(core::map<v3s16, NodeMod>::Iterator
+				i = m_mods.getIterator();
+				i.atEnd() == false; i++)
+		{
+			dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
+		}
+	}
+
+private:
+	core::map<v3s16, NodeMod> m_mods;
+};
+
 enum
 {
 	NODECONTAINER_ID_MAPBLOCK,
@@ -104,11 +172,26 @@ class MapBlock : public NodeContainer
 		return m_parent;
 	}
 
+	void reallocate()
+	{
+		if(data != NULL)
+			delete[] data;
+		u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
+		data = new MapNode[l];
+		for(u32 i=0; i<l; i++){
+			data[i] = MapNode();
+		}
+		setChangedFlag();
+	}
+
+	/*
+		Flags
+	*/
+
 	bool isDummy()
 	{
 		return (data == NULL);
 	}
-
 	void unDummify()
 	{
 		assert(isDummy());
@@ -119,16 +202,26 @@ class MapBlock : public NodeContainer
 	{
 		return changed;
 	}
-
 	void resetChangedFlag()
 	{
 		changed = false;
 	}
-
 	void setChangedFlag()
 	{
 		changed = true;
 	}
+
+	bool getIsUnderground()
+	{
+		return is_underground;
+	}
+
+	void setIsUnderground(bool a_is_underground)
+	{
+		is_underground = a_is_underground;
+		setChangedFlag();
+	}
+
 #ifndef SERVER
 	void setMeshExpired(bool expired)
 	{
@@ -140,6 +233,30 @@ class MapBlock : public NodeContainer
 		return m_mesh_expired;
 	}
 #endif
+
+	void setLightingExpired(bool expired)
+	{
+		m_lighting_expired = expired;
+		setChangedFlag();
+	}
+	bool getLightingExpired()
+	{
+		return m_lighting_expired;
+	}
+
+	bool isValid()
+	{
+		if(m_lighting_expired)
+			return false;
+		if(data == NULL)
+			return false;
+		return true;
+	}
+
+	/*
+		Position stuff
+	*/
+
 	v3s16 getPos()
 	{
 		return m_pos;
@@ -150,17 +267,6 @@ class MapBlock : public NodeContainer
 		return m_pos * MAP_BLOCKSIZE;
 	}
 		
-	bool getIsUnderground()
-	{
-		return is_underground;
-	}
-
-	void setIsUnderground(bool a_is_underground)
-	{
-		is_underground = a_is_underground;
-		setChangedFlag();
-	}
-
 	core::aabbox3d<s16> getBox()
 	{
 		return core::aabbox3d<s16>(getPosRelative(),
@@ -168,19 +274,11 @@ class MapBlock : public NodeContainer
 				+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
 				- v3s16(1,1,1));
 	}
-	
-	void reallocate()
-	{
-		if(data != NULL)
-			delete[] data;
-		u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
-		data = new MapNode[l];
-		for(u32 i=0; i<l; i++){
-			data[i] = MapNode();
-		}
-		setChangedFlag();
-	}
 
+	/*
+		Regular MapNode get-setters
+	*/
+	
 	bool isValidPosition(v3s16 p)
 	{
 		if(data == NULL)
@@ -190,10 +288,6 @@ class MapBlock : public NodeContainer
 				&& p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
 	}
 
-	/*
-		Regular MapNode get-setters
-	*/
-	
 	MapNode getNode(s16 x, s16 y, s16 z)
 	{
 		if(data == NULL)
@@ -271,9 +365,14 @@ class MapBlock : public NodeContainer
 					setNode(x0+x, y0+y, z0+z, node);
 	}
 
+	/*
+		Graphics-related methods
+	*/
+	
+	// A quick version with nodes passed as parameters
 	u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
 			v3s16 face_dir);
-	
+	// A more convenient version
 	u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
 	{
 		return getFaceLight(daynight_ratio,
@@ -288,11 +387,16 @@ class MapBlock : public NodeContainer
 			v3s16 dir, v3f scale, v3f posRelative_f,
 			core::array<FastFace> &dest);
 	
-	TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir);
-	u8 getNodeContent(v3s16 p, MapNode mn);
+	TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+			NodeModMap &temp_mods);
+	u8 getNodeContent(v3s16 p, MapNode mn,
+			NodeModMap &temp_mods);
 
 	/*
-		startpos:
+		Generates the FastFaces of a node row. This has a
+		ridiculous amount of parameters because that way they
+		can be precalculated by the caller.
+
 		translate_dir: unit vector with only one of x, y or z
 		face_dir: unit vector with only one of x, y or z
 	*/
@@ -305,12 +409,14 @@ class MapBlock : public NodeContainer
 			v3f translate_dir_f,
 			v3s16 face_dir,
 			v3f face_dir_f,
-			core::array<FastFace> &dest);
-
+			core::array<FastFace> &dest,
+			NodeModMap &temp_mods);
+	
+	/*
+		Thread-safely updates the whole mesh of the mapblock.
+	*/
 	void updateMesh(u32 daynight_ratio);
-	/*void updateMesh(s32 daynight_i);
-	// Updates all DAYNIGHT_CACHE_COUNT meshes
-	void updateMeshes(s32 first_i=0);*/
+	
 #endif // !SERVER
 	
 	// See comments in mapblock.cpp
@@ -322,7 +428,7 @@ class MapBlock : public NodeContainer
 	void copyTo(VoxelManipulator &dst);
 
 	/*
-		Object stuff
+		MapBlockObject stuff
 	*/
 	
 	void serializeObjects(std::ostream &os, u8 version)
@@ -384,9 +490,6 @@ class MapBlock : public NodeContainer
 		m_objects.getObjects(origin, max_d, dest);
 	}
 
-	/*void getPseudoObjects(v3f origin, f32 max_d,
-			core::array<DistanceSortedObject> &dest);*/
-
 	s32 getObjectCount()
 	{
 		return m_objects.getCount();
@@ -399,7 +502,7 @@ class MapBlock : public NodeContainer
 
 		returns true if the mod was different last time
 	*/
-	bool setTempMod(v3s16 p, NodeMod mod)
+	bool setTempMod(v3s16 p, const NodeMod &mod)
 	{
 		/*dstream<<"setTempMod called on block"
 				<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@@ -408,52 +511,33 @@ class MapBlock : public NodeContainer
 				<<std::endl;*/
 		JMutexAutoLock lock(m_temp_mods_mutex);
 
-		// See if old is different, cancel if it is not different.
-		core::map<v3s16, NodeMod>::Node *n = m_temp_mods.find(p);
-		if(n)
-		{
-			NodeMod old = n->getValue();
-			if(old == mod)
-				return false;
-		}
-
-		m_temp_mods[p] = mod;
-		return true;
+		return m_temp_mods.set(p, mod);
 	}
 	// Returns true if there was one
-	bool getTempMod(v3s16 p, struct NodeMod *mod)
+	bool getTempMod(v3s16 p, NodeMod *mod)
 	{
 		JMutexAutoLock lock(m_temp_mods_mutex);
-		core::map<v3s16, NodeMod>::Node *n;
-		n = m_temp_mods.find(p);
-		if(n == NULL)
-			return false;
-		if(mod)
-			*mod = n->getValue();
-		return true;
+
+		return m_temp_mods.get(p, mod);
 	}
 	bool clearTempMod(v3s16 p)
 	{
 		JMutexAutoLock lock(m_temp_mods_mutex);
-		if(m_temp_mods.find(p))
-		{
-			m_temp_mods.remove(p);
-			return true;
-		}
-		return false;
+
+		return m_temp_mods.clear(p);
 	}
 	bool clearTempMods()
 	{
 		JMutexAutoLock lock(m_temp_mods_mutex);
-		if(m_temp_mods.size() == 0)
-			return false;
-		m_temp_mods.clear();
-		return true;
+		
+		return m_temp_mods.clear();
 	}
 #endif
 
 	/*
-		Day-night lighting difference
+		Update day-night lighting difference flag.
+		
+		Sets m_day_night_differs to appropriate value.
 		
 		These methods don't care about neighboring blocks.
 		It means that to know if a block really doesn't need a mesh
@@ -490,18 +574,11 @@ class MapBlock : public NodeContainer
 
 	void deSerialize(std::istream &is, u8 version);
 
+private:
 	/*
-		Public member variables
+		Private methods
 	*/
 
-#ifndef SERVER
-	//scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
-	scene::SMesh *mesh;
-	JMutex mesh_mutex;
-#endif
-
-private:
-
 	/*
 		Used only internally, because changes can't be tracked
 	*/
@@ -520,28 +597,58 @@ class MapBlock : public NodeContainer
 		return getNodeRef(p.X, p.Y, p.Z);
 	}
 
+public:
+	/*
+		Public member variables
+	*/
+
+#ifndef SERVER
+	scene::SMesh *mesh;
+	JMutex mesh_mutex;
+#endif
 	
+private:
+	/*
+		Private member variables
+	*/
+
+	// Parent container (practically the Map)
+	// Not a MapSector, it is just a structural element.
 	NodeContainer *m_parent;
 	// Position in blocks on parent
 	v3s16 m_pos;
+	
 	/*
 		If NULL, block is a dummy block.
 		Dummy blocks are used for caching not-found-on-disk blocks.
 	*/
 	MapNode * data;
+
 	/*
-		- On the client, this is used for checking whether to
-		  recalculate the face cache. (Is it anymore?)
 		- On the server, this is used for telling whether the
 		  block has been changed from the one on disk.
+		- On the client, this is used for nothing.
 	*/
 	bool changed;
+
 	/*
-		Used for some initial lighting stuff.
-		At least /has been/ used. 8)
-		It's probably useless now.
+		When propagating sunlight and the above block doesn't exist,
+		sunlight is assumed if this is false.
+
+		In practice this is set to true if the block is completely
+		undeground with nothing visible above the ground except
+		caves.
 	*/
 	bool is_underground;
+
+	/*
+		Set to true if changes has been made that make the old lighting
+		values wrong but the lighting hasn't been actually updated.
+
+		If this is false, lighting is exactly right.
+		If this is true, lighting might be wrong or right.
+	*/
+	bool m_lighting_expired;
 	
 	// Whether day and night lighting differs
 	bool m_day_night_differs;
@@ -552,11 +659,16 @@ class MapBlock : public NodeContainer
 	float m_spawn_timer;
 	
 #ifndef SERVER
+	/*
+		Set to true if the mesh has been ordered to be updated
+		sometime in the background.
+		In practice this is set when the day/night lighting switches.
+	*/
 	bool m_mesh_expired;
 	
 	// Temporary modifications to nodes
 	// These are only used when drawing
-	core::map<v3s16, NodeMod> m_temp_mods;
+	NodeModMap m_temp_mods;
 	JMutex m_temp_mods_mutex;
 #endif
 };
diff --git a/src/mapchunk.h b/src/mapchunk.h
new file mode 100644
index 0000000000000000000000000000000000000000..284eebe606123a70f2d7b7d800bb8317131c08bf
--- /dev/null
+++ b/src/mapchunk.h
@@ -0,0 +1,51 @@
+/*
+Minetest-c55
+Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef MAPCHUNK_HEADER
+#define MAPCHUNK_HEADER
+
+/*
+	MapChunk contains map-generation-time metadata for an area of
+	some MapSectors. (something like 64x64)
+*/
+
+class MapChunk
+{
+public:
+	MapChunk():
+		m_is_volatile(true)
+	{
+	}
+
+	/*
+		If is_volatile is true, chunk can be modified when
+		neighboring chunks are generated.
+
+		It is set to false when all the 8 neighboring chunks have
+		been generated.
+	*/
+	bool getIsVolatile(){ return m_is_volatile; }
+	void setIsVolatile(bool is){ m_is_volatile = is; }
+
+private:
+	bool m_is_volatile;
+};
+
+#endif
+
diff --git a/src/mapsector.cpp b/src/mapsector.cpp
index 8a3728c8f2a0ddedd29933efd35b375e0b41befd..fa1bb68d0dc861e4e83f9844409d336e5d82d915 100644
--- a/src/mapsector.cpp
+++ b/src/mapsector.cpp
@@ -82,11 +82,16 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
 	return block;
 }
 
-MapBlock * MapSector::getBlockNoCreate(s16 y)
+MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
 {
 	JMutexAutoLock lock(m_mutex);
 	
-	MapBlock *block = getBlockBuffered(y);
+	return getBlockBuffered(y);
+}
+
+MapBlock * MapSector::getBlockNoCreate(s16 y)
+{
+	MapBlock *block = getBlockNoCreateNoEx(y);
 
 	if(block == NULL)
 		throw InvalidPositionException();
diff --git a/src/mapsector.h b/src/mapsector.h
index 0c32e2606f07b3554b5ef91172f9cd71226c6428..de93806b5a88703b91c7885eb2b6e8c67f211db0 100644
--- a/src/mapsector.h
+++ b/src/mapsector.h
@@ -67,6 +67,7 @@ class MapSector: public NodeContainer, public Heightmappish
 		return m_pos;
 	}
 
+	MapBlock * getBlockNoCreateNoEx(s16 y);
 	MapBlock * getBlockNoCreate(s16 y);
 	MapBlock * createBlankBlockNoInsert(s16 y);
 	MapBlock * createBlankBlock(s16 y);
diff --git a/src/server.cpp b/src/server.cpp
index 823a48b900b7d86b2424b26ea8627545ddb6878c..80aa47671849809a9af105f11a190981c03f94a3 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -156,20 +156,18 @@ void * EmergeThread::Thread()
 				only_from_disk = true;
 			
 			// First check if the block already exists
-			if(only_from_disk)
-			{
-				block = map.getBlockNoCreate(p);
-			}
+			//block = map.getBlockNoCreate(p);
 
 			if(block == NULL)
 			{
+				//dstream<<"Calling emergeBlock"<<std::endl;
 				block = map.emergeBlock(
 						p,
 						only_from_disk,
 						changed_blocks,
 						lighting_invalidated_blocks);
 
-#if 1
+#if 0
 				/*
 					EXPERIMENTAL: Create a few other blocks too
 				*/
@@ -206,6 +204,12 @@ void * EmergeThread::Thread()
 			{
 				//dstream<<"EmergeThread: Got a dummy block"<<std::endl;
 				got_block = false;
+
+				if(only_from_disk == false)
+				{
+					dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
+					assert(0);
+				}
 			}
 		}
 		catch(InvalidPositionException &e)
@@ -581,16 +585,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			/*
 				Check if map has this block
 			*/
-			MapBlock *block = NULL;
-			try
-			{
-				block = server->m_env.getMap().getBlockNoCreate(p);
-			}
-			catch(InvalidPositionException &e)
-			{
-			}
+			MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
 			
 			bool surely_not_found_on_disk = false;
+			bool block_is_invalid = false;
 			if(block != NULL)
 			{
 				/*if(block->isIncomplete())
@@ -603,6 +601,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 				{
 					surely_not_found_on_disk = true;
 				}
+
+				if(block->isValid() == false)
+				{
+					block_is_invalid = true;
+				}
 			}
 
 			/*
@@ -627,8 +630,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			/*
 				Add inexistent block to emerge queue.
 			*/
-			if(block == NULL || surely_not_found_on_disk)
+			if(block == NULL || surely_not_found_on_disk || block_is_invalid)
 			{
+				//dstream<<"asd"<<std::endl;
+				
 				/*SharedPtr<JMutexAutoLock> lock
 						(m_num_blocks_in_emerge_queue.getLock());*/
 				
@@ -636,6 +641,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 				// Allow only one block in emerge queue
 				if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
 				{
+					//dstream<<"Adding block to emerge queue"<<std::endl;
+					
 					// Add it to the emerge queue and trigger the thread
 					
 					u8 flags = 0;
diff --git a/src/utility.h b/src/utility.h
index 055c8db1a15d41b19f96830f99bbc6b78100343c..a61de1c37e7815d92e94e9b7b47b801eac39523d 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -538,6 +538,23 @@ inline v3s16 getContainerPos(v3s16 p, s16 d)
 	);
 }
 
+inline v2s16 getContainerPos(v2s16 p, v2s16 d)
+{
+	return v2s16(
+		getContainerPos(p.X, d.X),
+		getContainerPos(p.Y, d.Y)
+	);
+}
+
+inline v3s16 getContainerPos(v3s16 p, v3s16 d)
+{
+	return v3s16(
+		getContainerPos(p.X, d.X),
+		getContainerPos(p.Y, d.Y),
+		getContainerPos(p.Z, d.Z)
+	);
+}
+
 inline bool isInArea(v3s16 p, s16 d)
 {
 	return (
diff --git a/src/voxel.cpp b/src/voxel.cpp
index d68a8db0211eb7bd14afed2b0988e9c9962275dc..02635d3af54a6ae4548ad6f2b4ebf45903a2f848 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -536,10 +536,13 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
 	}
 }
 
-#if 1
+#if 0
 /*
 	Lights neighbors of from_nodes, collects all them and then
 	goes on recursively.
+
+	NOTE: This is faster in small areas but will overflow the
+	      stack on large areas. Thus it is not used.
 */
 void VoxelManipulator::spreadLight(enum LightBank bank,
 		core::map<v3s16, bool> & from_nodes)
@@ -560,7 +563,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
 }
 #endif
 
-#if 0
+#if 1
 /*
 	Lights neighbors of from_nodes, collects all them and then
 	goes on recursively.