From 3909e712a012c11793effc408fd348e438a9ac5b Mon Sep 17 00:00:00 2001
From: Perttu Ahola <celeron55@gmail.com>
Date: Mon, 24 Jan 2011 16:36:58 +0200
Subject: [PATCH] Faster lighting at map generation time

---
 src/defaultsettings.cpp |   1 +
 src/main.cpp            |   3 +-
 src/map.cpp             | 141 ++++++++++++++-
 src/map.h               |  15 +-
 src/player.cpp          |   5 +-
 src/server.cpp          |  66 ++++---
 src/voxel.cpp           | 389 ++++++++++++++++++++++++++++++++++++++++
 src/voxel.h             |  18 +-
 8 files changed, 605 insertions(+), 33 deletions(-)

diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 6cd242f01..4046b81b9 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -47,6 +47,7 @@ void set_default_settings()
 	g_settings.setDefault("objectdata_interval", "0.2");
 	g_settings.setDefault("active_object_range", "2");
 	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");
diff --git a/src/main.cpp b/src/main.cpp
index 092d62988..d2f8a6b9d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -259,7 +259,8 @@ TODO: Remove HMParams
 
 TODO: Flowing water to actually contain flow direction information
 
-TODO: Faster lighting using VoxelManipulator
+TODO: Remove duplicate lighting implementation from Map (leave
+      VoxelManipulator)
 
 Doing now:
 ----------
diff --git a/src/map.cpp b/src/map.cpp
index 09fb154aa..bbcc0f36f 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -622,10 +622,13 @@ void Map::updateLighting(enum LightBank bank,
 		core::map<v3s16, MapBlock*> & modified_blocks)
 {
 	/*m_dout<<DTIME<<"Map::updateLighting(): "
-			<<a_blocks.getSize()<<" blocks... ";*/
+			<<a_blocks.size()<<" blocks."<<std::endl;*/
+	
+	//TimeTaker timer("updateLighting");
 	
 	// For debugging
-	bool debug=false;
+	bool debug=true;
+
 	u32 count_was = modified_blocks.size();
 
 	core::map<v3s16, bool> light_sources;
@@ -720,9 +723,10 @@ void Map::updateLighting(enum LightBank bank,
 			
 		}
 	}
-	
+
+#if 0
 	{
-		//TimeTaker timer("unspreadLight");
+		TimeTaker timer("unspreadLight");
 		unspreadLight(bank, unlight_from, light_sources, modified_blocks);
 	}
 	
@@ -741,7 +745,7 @@ void Map::updateLighting(enum LightBank bank,
 	//       - Find out why it works
 
 	{
-		//TimeTaker timer("spreadLight");
+		TimeTaker timer("spreadLight");
 		spreadLight(bank, light_sources, modified_blocks);
 	}
 	
@@ -751,6 +755,36 @@ void Map::updateLighting(enum LightBank bank,
 		count_was = modified_blocks.size();
 		dstream<<"spreadLight modified "<<diff<<std::endl;
 	}
+#endif
+	
+	{
+		//MapVoxelManipulator vmanip(this);
+
+		ManualMapVoxelManipulator vmanip(this);
+		
+		core::map<v3s16, MapBlock*>::Iterator i;
+		i = a_blocks.getIterator();
+		for(; i.atEnd() == false; i++)
+		{
+			MapBlock *block = i.getNode()->getValue();
+			v3s16 p = block->getPos();
+			vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
+		}
+		{
+			//TimeTaker timer("unSpreadLight");
+			vmanip.unspreadLight(bank, unlight_from, light_sources);
+		}
+		{
+			//TimeTaker timer("spreadLight");
+			vmanip.spreadLight(bank, light_sources);
+		}
+		{
+			//TimeTaker timer("blitBack");
+			vmanip.blitBack(modified_blocks);
+		}
+		/*dstream<<"emerge_time="<<emerge_time<<std::endl;
+		emerge_time = 0;*/
+	}
 
 	//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
 }
@@ -2220,7 +2254,8 @@ MapBlock * ServerMap::emergeBlock(
 	}
 
 	//dstream<<"Not found on disk, generating."<<std::endl;
-	//TimeTaker("emergeBlock()", g_irrlicht);
+	// 0ms
+	//TimeTaker("emergeBlock() generate");
 
 	/*
 		Do not generate over-limit
@@ -4138,7 +4173,10 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 #endif
 
 #if 0
-void MapVoxelManipulator::emerge(VoxelArea a)
+/*
+	NOTE: This is slow
+*/
+void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 {
 	TimeTaker timer1("emerge", &emerge_time);
 	
@@ -4185,6 +4223,9 @@ void MapVoxelManipulator::blitBack
 		return;
 	
 	//TimeTaker timer1("blitBack");
+
+	/*dstream<<"blitBack(): m_loaded_blocks.size()="
+			<<m_loaded_blocks.size()<<std::endl;*/
 	
 	/*
 		Initialize block cache
@@ -4241,4 +4282,90 @@ void MapVoxelManipulator::blitBack
 	}
 }
 
+ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
+		MapVoxelManipulator(map)
+{
+}
+
+ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
+{
+}
+
+void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
+{
+	// Just create the area to avoid segfaults
+	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(
+		v3s16 blockpos_min, v3s16 blockpos_max)
+{
+	TimeTaker timer1("emerge", &emerge_time);
+
+	// Units of these are MapBlocks
+	v3s16 p_min = blockpos_min;
+	v3s16 p_max = blockpos_max;
+
+	VoxelArea block_area_nodes
+			(p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
+
+	addArea(block_area_nodes);
+
+	for(s32 z=p_min.Z; z<=p_max.Z; z++)
+	for(s32 y=p_min.Y; y<=p_max.Y; y++)
+	for(s32 x=p_min.X; x<=p_max.X; x++)
+	{
+		v3s16 p(x,y,z);
+		core::map<v3s16, bool>::Node *n;
+		n = m_loaded_blocks.find(p);
+		if(n != NULL)
+			continue;
+		
+		bool block_data_inexistent = false;
+		try
+		{
+			TimeTaker timer1("emerge load", &emerge_load_time);
+
+			MapBlock *block = m_map->getBlockNoCreate(p);
+			if(block->isDummy())
+				block_data_inexistent = true;
+			else
+				block->copyTo(*this);
+		}
+		catch(InvalidPositionException &e)
+		{
+			block_data_inexistent = true;
+		}
+
+		if(block_data_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++)
+			for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
+			{
+				s32 i = m_area.index(a.MinEdge.X,y,z);
+				memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
+			}
+		}
+
+		m_loaded_blocks.insert(p, true);
+	}
+}
+
 //END
diff --git a/src/map.h b/src/map.h
index 3b6b169e5..787e1240f 100644
--- a/src/map.h
+++ b/src/map.h
@@ -523,7 +523,7 @@ class MapVoxelManipulator : public VoxelManipulator
 
 	void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
 
-private:
+protected:
 	Map *m_map;
 	/*
 		NOTE: This might be used or not
@@ -534,5 +534,18 @@ class MapVoxelManipulator : public VoxelManipulator
 	core::map<v3s16, bool> m_loaded_blocks;
 };
 
+class ManualMapVoxelManipulator : public MapVoxelManipulator
+{
+public:
+	ManualMapVoxelManipulator(Map *map);
+	virtual ~ManualMapVoxelManipulator();
+	
+	virtual void emerge(VoxelArea a, s32 caller_id=-1);
+
+	void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
+
+protected:
+};
+
 #endif
 
diff --git a/src/player.cpp b/src/player.cpp
index bb25015b5..8aabb030c 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -412,8 +412,9 @@ void LocalPlayer::applyControl(float dtime)
 		}
 		else
 		{
-			speed += move_direction;
-			superspeed = true;
+			// "Turbo button"
+			/*speed += move_direction;
+			superspeed = true;*/
 		}
 	}
 
diff --git a/src/server.cpp b/src/server.cpp
index 65e390345..716ff77c6 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -168,7 +168,7 @@ void * EmergeThread::Thread()
 						changed_blocks,
 						lighting_invalidated_blocks);
 
-#if 0
+#if 1
 				/*
 					EXPERIMENTAL: Create a few other blocks too
 				*/
@@ -184,6 +184,19 @@ void * EmergeThread::Thread()
 						only_from_disk,
 						changed_blocks,
 						lighting_invalidated_blocks);
+#if 0
+				map.emergeBlock(
+						p + v3s16(0,2,0),
+						only_from_disk,
+						changed_blocks,
+						lighting_invalidated_blocks);
+
+				map.emergeBlock(
+						p + v3s16(0,-2,0),
+						only_from_disk,
+						changed_blocks,
+						lighting_invalidated_blocks);
+#endif
 #endif
 			}
 
@@ -216,23 +229,6 @@ void * EmergeThread::Thread()
 				dout_server<<std::endl;
 			}
 
-#if 0
-			/*
-				Update water pressure
-			*/
-
-			m_server->UpdateBlockWaterPressure(block, modified_blocks);
-
-			for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
-					i.atEnd() == false; i++)
-			{
-				MapBlock *block = i.getNode()->getValue();
-				m_server->UpdateBlockWaterPressure(block, modified_blocks);
-				//v3s16 p = i.getNode()->getKey();
-				//m_server->UpdateBlockWaterPressure(p, modified_blocks);
-			}
-#endif
-
 			/*
 				Collect a list of blocks that have been modified in
 				addition to the fetched one.
@@ -249,7 +245,7 @@ void * EmergeThread::Thread()
 			/*dstream<<"lighting "<<lighting_invalidated_blocks.size()
 					<<" blocks"<<std::endl;*/
 			
-			//TimeTaker timer("** updateLighting", g_device);
+			//TimeTaker timer("** updateLighting");
 			
 			// Update lighting without locking the environment mutex,
 			// add modified blocks to changed blocks
@@ -497,7 +493,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 			|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 			|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
 				continue;
-
+		
+			// If this is true, inexistent block will be made from scratch
 			bool generate = d <= d_max_gen;
 			
 			if(haxmode)
@@ -513,6 +510,35 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
 					generate = false;
 			}
 
+			/*
+				If block is far away, don't generate it unless it is
+				near ground level
+			*/
+			if(d > 4)
+			{
+				v2s16 p2d(p.X, p.Z);
+				MapSector *sector = NULL;
+				try
+				{
+					sector = server->m_env.getMap().getSectorNoGenerate(p2d);
+				}
+				catch(InvalidPositionException &e)
+				{
+				}
+
+				if(sector != NULL)
+				{
+					// Get center ground height in nodes
+					f32 gh = sector->getGroundHeight(
+							v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
+					// Block center y in nodes
+					f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
+					// If differs a lot, don't generate
+					if(fabs(gh - y) > MAP_BLOCKSIZE*2)
+						generate = false;
+				}
+			}
+
 			/*
 				Don't draw if not in sight
 			*/
diff --git a/src/voxel.cpp b/src/voxel.cpp
index c045c949c..a0cc44d71 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -264,6 +264,395 @@ void VoxelManipulator::clearFlag(u8 flags)
 			<<volume<<" nodes"<<std::endl;*/
 }
 
+void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
+		core::map<v3s16, bool> & light_sources)
+{
+	v3s16 dirs[6] = {
+		v3s16(0,0,1), // back
+		v3s16(0,1,0), // top
+		v3s16(1,0,0), // right
+		v3s16(0,0,-1), // front
+		v3s16(0,-1,0), // bottom
+		v3s16(-1,0,0), // left
+	};
+	
+	emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
+
+	// Loop through 6 neighbors
+	for(u16 i=0; i<6; i++)
+	{
+		// Get the position of the neighbor node
+		v3s16 n2pos = p + dirs[i];
+		
+		u32 n2i = m_area.index(n2pos);
+
+		if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
+			continue;
+
+		MapNode &n2 = m_data[n2i];
+		
+		/*
+			If the neighbor is dimmer than what was specified
+			as oldlight (the light of the previous node)
+		*/
+		if(n2.getLight(bank) < oldlight)
+		{
+			/*
+				And the neighbor is transparent and it has some light
+			*/
+			if(n2.light_propagates() && n2.getLight(bank) != 0)
+			{
+				/*
+					Set light to 0 and add to queue
+				*/
+
+				u8 current_light = n2.getLight(bank);
+				n2.setLight(bank, 0);
+				
+				unspreadLight(bank, n2pos, current_light, light_sources);
+				
+				/*
+					Remove from light_sources if it is there
+					NOTE: This doesn't happen nearly at all
+				*/
+				/*if(light_sources.find(n2pos))
+				{
+					std::cout<<"Removed from light_sources"<<std::endl;
+					light_sources.remove(n2pos);
+				}*/
+			}
+		}
+		else{
+			light_sources.insert(n2pos, true);
+		}
+	}
+}
+
+#if 1
+/*
+	Goes recursively through the neighbours of the node.
+
+	Alters only transparent nodes.
+
+	If the lighting of the neighbour is lower than the lighting of
+	the node was (before changing it to 0 at the step before), the
+	lighting of the neighbour is set to 0 and then the same stuff
+	repeats for the neighbour.
+
+	The ending nodes of the routine are stored in light_sources.
+	This is useful when a light is removed. In such case, this
+	routine can be called for the light node and then again for
+	light_sources to re-light the area without the removed light.
+
+	values of from_nodes are lighting values.
+*/
+void VoxelManipulator::unspreadLight(enum LightBank bank,
+		core::map<v3s16, u8> & from_nodes,
+		core::map<v3s16, bool> & light_sources)
+{
+	if(from_nodes.size() == 0)
+		return;
+	
+	core::map<v3s16, u8>::Iterator j;
+	j = from_nodes.getIterator();
+
+	for(; j.atEnd() == false; j++)
+	{
+		v3s16 pos = j.getNode()->getKey();
+		
+		//MapNode &n = m_data[m_area.index(pos)];
+		
+		u8 oldlight = j.getNode()->getValue();
+
+		unspreadLight(bank, pos, oldlight, light_sources);
+	}
+}
+#endif
+
+#if 0
+/*
+	Goes recursively through the neighbours of the node.
+
+	Alters only transparent nodes.
+
+	If the lighting of the neighbour is lower than the lighting of
+	the node was (before changing it to 0 at the step before), the
+	lighting of the neighbour is set to 0 and then the same stuff
+	repeats for the neighbour.
+
+	The ending nodes of the routine are stored in light_sources.
+	This is useful when a light is removed. In such case, this
+	routine can be called for the light node and then again for
+	light_sources to re-light the area without the removed light.
+
+	values of from_nodes are lighting values.
+*/
+void VoxelManipulator::unspreadLight(enum LightBank bank,
+		core::map<v3s16, u8> & from_nodes,
+		core::map<v3s16, bool> & light_sources)
+{
+	v3s16 dirs[6] = {
+		v3s16(0,0,1), // back
+		v3s16(0,1,0), // top
+		v3s16(1,0,0), // right
+		v3s16(0,0,-1), // front
+		v3s16(0,-1,0), // bottom
+		v3s16(-1,0,0), // left
+	};
+	
+	if(from_nodes.size() == 0)
+		return;
+	
+	core::map<v3s16, u8> unlighted_nodes;
+	core::map<v3s16, u8>::Iterator j;
+	j = from_nodes.getIterator();
+
+	for(; j.atEnd() == false; j++)
+	{
+		v3s16 pos = j.getNode()->getKey();
+		
+		emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
+
+		//MapNode &n = m_data[m_area.index(pos)];
+		
+		u8 oldlight = j.getNode()->getValue();
+		
+		// Loop through 6 neighbors
+		for(u16 i=0; i<6; i++)
+		{
+			// Get the position of the neighbor node
+			v3s16 n2pos = pos + dirs[i];
+			
+			u32 n2i = m_area.index(n2pos);
+
+			if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
+				continue;
+
+			MapNode &n2 = m_data[n2i];
+			
+			/*
+				If the neighbor is dimmer than what was specified
+				as oldlight (the light of the previous node)
+			*/
+			if(n2.getLight(bank) < oldlight)
+			{
+				/*
+					And the neighbor is transparent and it has some light
+				*/
+				if(n2.light_propagates() && n2.getLight(bank) != 0)
+				{
+					/*
+						Set light to 0 and add to queue
+					*/
+
+					u8 current_light = n2.getLight(bank);
+					n2.setLight(bank, 0);
+
+					unlighted_nodes.insert(n2pos, current_light);
+					
+					/*
+						Remove from light_sources if it is there
+						NOTE: This doesn't happen nearly at all
+					*/
+					/*if(light_sources.find(n2pos))
+					{
+						std::cout<<"Removed from light_sources"<<std::endl;
+						light_sources.remove(n2pos);
+					}*/
+				}
+			}
+			else{
+				light_sources.insert(n2pos, true);
+			}
+		}
+	}
+
+	/*dstream<<"unspreadLight(): Changed block "
+			<<blockchangecount<<" times"
+			<<" for "<<from_nodes.size()<<" nodes"
+			<<std::endl;*/
+	
+	if(unlighted_nodes.size() > 0)
+		unspreadLight(bank, unlighted_nodes, light_sources);
+}
+#endif
+
+void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
+{
+	const v3s16 dirs[6] = {
+		v3s16(0,0,1), // back
+		v3s16(0,1,0), // top
+		v3s16(1,0,0), // right
+		v3s16(0,0,-1), // front
+		v3s16(0,-1,0), // bottom
+		v3s16(-1,0,0), // left
+	};
+
+	emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
+
+	u32 i = m_area.index(p);
+	
+	if(m_flags[i] & VOXELFLAG_INEXISTENT)
+		return;
+
+	MapNode &n = m_data[i];
+
+	u8 oldlight = n.getLight(bank);
+	u8 newlight = diminish_light(oldlight);
+
+	// Loop through 6 neighbors
+	for(u16 i=0; i<6; i++)
+	{
+		// Get the position of the neighbor node
+		v3s16 n2pos = p + dirs[i];
+		
+		u32 n2i = m_area.index(n2pos);
+
+		if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
+			continue;
+
+		MapNode &n2 = m_data[n2i];
+		
+		/*
+			If the neighbor is brighter than the current node,
+			add to list (it will light up this node on its turn)
+		*/
+		if(n2.getLight(bank) > undiminish_light(oldlight))
+		{
+			spreadLight(bank, n2pos);
+		}
+		/*
+			If the neighbor is dimmer than how much light this node
+			would spread on it, add to list
+		*/
+		if(n2.getLight(bank) < newlight)
+		{
+			if(n2.light_propagates())
+			{
+				n2.setLight(bank, newlight);
+				spreadLight(bank, n2pos);
+			}
+		}
+	}
+}
+
+#if 1
+/*
+	Lights neighbors of from_nodes, collects all them and then
+	goes on recursively.
+*/
+void VoxelManipulator::spreadLight(enum LightBank bank,
+		core::map<v3s16, bool> & from_nodes)
+{
+	if(from_nodes.size() == 0)
+		return;
+	
+	core::map<v3s16, bool> lighted_nodes;
+	core::map<v3s16, bool>::Iterator j;
+	j = from_nodes.getIterator();
+
+	for(; j.atEnd() == false; j++)
+	{
+		v3s16 pos = j.getNode()->getKey();
+
+		spreadLight(bank, pos);
+	}
+}
+#endif
+
+#if 0
+/*
+	Lights neighbors of from_nodes, collects all them and then
+	goes on recursively.
+*/
+void VoxelManipulator::spreadLight(enum LightBank bank,
+		core::map<v3s16, bool> & from_nodes)
+{
+	const v3s16 dirs[6] = {
+		v3s16(0,0,1), // back
+		v3s16(0,1,0), // top
+		v3s16(1,0,0), // right
+		v3s16(0,0,-1), // front
+		v3s16(0,-1,0), // bottom
+		v3s16(-1,0,0), // left
+	};
+
+	if(from_nodes.size() == 0)
+		return;
+	
+	core::map<v3s16, bool> lighted_nodes;
+	core::map<v3s16, bool>::Iterator j;
+	j = from_nodes.getIterator();
+
+	for(; j.atEnd() == false; j++)
+	{
+		v3s16 pos = j.getNode()->getKey();
+
+		emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
+
+		u32 i = m_area.index(pos);
+		
+		if(m_flags[i] & VOXELFLAG_INEXISTENT)
+			continue;
+
+		MapNode &n = m_data[i];
+
+		u8 oldlight = n.getLight(bank);
+		u8 newlight = diminish_light(oldlight);
+
+		// Loop through 6 neighbors
+		for(u16 i=0; i<6; i++)
+		{
+			// Get the position of the neighbor node
+			v3s16 n2pos = pos + dirs[i];
+			
+			try
+			{
+				u32 n2i = m_area.index(n2pos);
+
+				if(m_flags[n2i] & VOXELFLAG_INEXISTENT)
+					continue;
+
+				MapNode &n2 = m_data[n2i];
+				
+				/*
+					If the neighbor is brighter than the current node,
+					add to list (it will light up this node on its turn)
+				*/
+				if(n2.getLight(bank) > undiminish_light(oldlight))
+				{
+					lighted_nodes.insert(n2pos, true);
+				}
+				/*
+					If the neighbor is dimmer than how much light this node
+					would spread on it, add to list
+				*/
+				if(n2.getLight(bank) < newlight)
+				{
+					if(n2.light_propagates())
+					{
+						n2.setLight(bank, newlight);
+						lighted_nodes.insert(n2pos, true);
+					}
+				}
+			}
+			catch(InvalidPositionException &e)
+			{
+				continue;
+			}
+		}
+	}
+
+	/*dstream<<"spreadLight(): Changed block "
+			<<blockchangecount<<" times"
+			<<" for "<<from_nodes.size()<<" nodes"
+			<<std::endl;*/
+	
+	if(lighted_nodes.size() > 0)
+		spreadLight(bank, lighted_nodes);
+}
+#endif
+
 #if 0
 int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
 {
diff --git a/src/voxel.h b/src/voxel.h
index c377dfe7a..80d292891 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -323,7 +323,11 @@ class VoxelManipulator /*: public NodeContainer*/
 		emerge(p);
 		return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
 	}*/
-	// These are a bit slow and shouldn't be used internally
+
+	/*
+		These are a bit slow and shouldn't be used internally.
+		Use m_data[m_area.index(p)] instead.
+	*/
 	MapNode getNode(v3s16 p)
 	{
 		emerge(p);
@@ -396,7 +400,17 @@ class VoxelManipulator /*: public NodeContainer*/
 	*/
 
 	void clearFlag(u8 flag);
-
+	
+	void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
+			core::map<v3s16, bool> & light_sources);
+	void unspreadLight(enum LightBank bank,
+			core::map<v3s16, u8> & from_nodes,
+			core::map<v3s16, bool> & light_sources);
+	
+	void spreadLight(enum LightBank bank, v3s16 p);
+	void spreadLight(enum LightBank bank,
+			core::map<v3s16, bool> & from_nodes);
+	
 #if 0
 	// VOXELFLAG_CHECKED2s must usually be cleared before calling
 	// -1: dead end, 0-255: pressure
-- 
GitLab