Skip to content
Snippets Groups Projects
map.cpp 97 KiB
Newer Older
Perttu Ahola's avatar
Perttu Ahola committed
/*
Perttu Ahola's avatar
Perttu Ahola committed
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.
Perttu Ahola's avatar
Perttu Ahola committed
*/

#include "map.h"
#include "main.h"
#include "jmutexautolock.h"
#include "client.h"
#include "filesys.h"
#include "utility.h"
#include "voxel.h"
Perttu Ahola's avatar
Perttu Ahola committed
#include "porting.h"
Perttu Ahola's avatar
Perttu Ahola committed

Perttu Ahola's avatar
Perttu Ahola committed
Map::Map(std::ostream &dout):
	m_dout(dout),
	m_camera_position(0,0,0),
	m_camera_direction(0,0,1),
	m_sector_cache(NULL),
	m_hwrapper(this)
Perttu Ahola's avatar
Perttu Ahola committed
{
	m_sector_mutex.Init();
	m_camera_mutex.Init();
	assert(m_sector_mutex.IsInitialized());
	assert(m_camera_mutex.IsInitialized());
	
	// Get this so that the player can stay on it at first
	//getSector(v2s16(0,0));
}

Map::~Map()
{
	/*
		Stop updater thread
	*/
	/*updater.setRun(false);
	while(updater.IsRunning())
		sleep_s(1);*/

	/*
		Free all MapSectors.
	*/
	core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
	for(; i.atEnd() == false; i++)
	{
		MapSector *sector = i.getNode()->getValue();
		delete sector;
	}
}

MapSector * Map::getSectorNoGenerate(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
		sector->usage_timer = 0.0;
		return sector;
	}
	
	core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
	// If sector doesn't exist, throw an exception
	if(n == NULL)
	{
		throw InvalidPositionException();
	}
	
	MapSector *sector = n->getValue();
	
	// Cache the last result
	m_sector_cache_p = p;
	m_sector_cache = sector;

	//MapSector * ref(sector);
	
	// Reset inactivity timer
	sector->usage_timer = 0.0;
	return sector;
}

MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{	
	v2s16 p2d(p3d.X, p3d.Z);
	MapSector * sector = getSectorNoGenerate(p2d);

	MapBlock *block = sector->getBlockNoCreate(p3d.Y);

	return block;
}

Perttu Ahola's avatar
Perttu Ahola committed
MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
{
	try
	{
		v2s16 p2d(p3d.X, p3d.Z);
		MapSector * sector = getSectorNoGenerate(p2d);
		MapBlock *block = sector->getBlockNoCreate(p3d.Y);
		return block;
	}
	catch(InvalidPositionException &e)
	{
		return NULL;
	}
}

Perttu Ahola's avatar
Perttu Ahola committed
f32 Map::getGroundHeight(v2s16 p, bool generate)
{
	try{
		v2s16 sectorpos = getNodeSectorPos(p);
		MapSector * sref = getSectorNoGenerate(sectorpos);
		v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
		f32 y = sref->getGroundHeight(relpos);
		return y;
	}
	catch(InvalidPositionException &e)
	{
		return GROUNDHEIGHT_NOTFOUND_SETVALUE;
	}
}

void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
{
	/*m_dout<<DTIME<<"Map::setGroundHeight(("
			<<p.X<<","<<p.Y
			<<"), "<<y<<")"<<std::endl;*/
	v2s16 sectorpos = getNodeSectorPos(p);
	MapSector * sref = getSectorNoGenerate(sectorpos);
	v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
	//sref->mutex.Lock();
	sref->setGroundHeight(relpos, y);
	//sref->mutex.Unlock();
}

bool Map::isNodeUnderground(v3s16 p)
{
	v3s16 blockpos = getNodeBlockPos(p);
	try{
		MapBlock * block = getBlockNoCreate(blockpos);
		return block->getIsUnderground();
	}
	catch(InvalidPositionException &e)
	{
		return false;
	}
}

/*
	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.
*/
Perttu Ahola's avatar
Perttu Ahola committed
void Map::unspreadLight(enum LightBank bank,
		core::map<v3s16, u8> & from_nodes,
Perttu Ahola's avatar
Perttu Ahola committed
		core::map<v3s16, bool> & light_sources,
		core::map<v3s16, MapBlock*>  & modified_blocks)
{
	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;
	
	u32 blockchangecount = 0;

	core::map<v3s16, u8> unlighted_nodes;
	core::map<v3s16, u8>::Iterator j;
	j = from_nodes.getIterator();

	/*
		Initialize block cache
	*/
	v3s16 blockpos_last;
	MapBlock *block = NULL;
	// Cache this a bit, too
	bool block_checked_in_modified = false;
	
	for(; j.atEnd() == false; j++)
	{
		v3s16 pos = j.getNode()->getKey();
		v3s16 blockpos = getNodeBlockPos(pos);
		
		// Only fetch a new block if the block position has changed
		try{
			if(block == NULL || blockpos != blockpos_last){
				block = getBlockNoCreate(blockpos);
				blockpos_last = blockpos;

				block_checked_in_modified = false;
				blockchangecount++;
			}
		}
		catch(InvalidPositionException &e)
		{
			continue;
		}

		if(block->isDummy())
			continue;

		// Calculate relative position in block
		v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;

		// Get node straight from the block
		MapNode n = block->getNode(relpos);
		
		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];
			
			// Get the block where the node is located
			v3s16 blockpos = getNodeBlockPos(n2pos);

			try
			{
				// Only fetch a new block if the block position has changed
				try{
					if(block == NULL || blockpos != blockpos_last){
						block = getBlockNoCreate(blockpos);
						blockpos_last = blockpos;

						block_checked_in_modified = false;
						blockchangecount++;
					}
				}
				catch(InvalidPositionException &e)
				{
					continue;
				}
				
				// Calculate relative position in block
				v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
				// Get node straight from the block
				MapNode n2 = block->getNode(relpos);
				
				bool changed = false;

				//TODO: Optimize output by optimizing light_sources?

				/*
					If the neighbor is dimmer than what was specified
					as oldlight (the light of the previous node)
				*/
Perttu Ahola's avatar
Perttu Ahola committed
				if(n2.getLight(bank) < oldlight)
Perttu Ahola's avatar
Perttu Ahola committed
				{
					/*
						And the neighbor is transparent and it has some light
					*/
Perttu Ahola's avatar
Perttu Ahola committed
					if(n2.light_propagates() && n2.getLight(bank) != 0)
Perttu Ahola's avatar
Perttu Ahola committed
					{
						/*
							Set light to 0 and add to queue
						*/

Perttu Ahola's avatar
Perttu Ahola committed
						u8 current_light = n2.getLight(bank);
						n2.setLight(bank, 0);
Perttu Ahola's avatar
Perttu Ahola committed
						block->setNode(relpos, n2);

						unlighted_nodes.insert(n2pos, current_light);
						changed = true;

						/*
							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);
						}*/
					}
Perttu Ahola's avatar
Perttu Ahola committed
					
					/*// DEBUG
					if(light_sources.find(n2pos) != NULL)
						light_sources.remove(n2pos);*/
Perttu Ahola's avatar
Perttu Ahola committed
				}
				else{
					light_sources.insert(n2pos, true);
				}

				// Add to modified_blocks
				if(changed == true && block_checked_in_modified == false)
				{
					// If the block is not found in modified_blocks, add.
					if(modified_blocks.find(blockpos) == NULL)
					{
						modified_blocks.insert(blockpos, block);
					}
					block_checked_in_modified = true;
				}
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}
		}
	}

	/*dstream<<"unspreadLight(): Changed block "
			<<blockchangecount<<" times"
			<<" for "<<from_nodes.size()<<" nodes"
			<<std::endl;*/
	
	if(unlighted_nodes.size() > 0)
Perttu Ahola's avatar
Perttu Ahola committed
		unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
}

/*
	A single-node wrapper of the above
*/
Perttu Ahola's avatar
Perttu Ahola committed
void Map::unLightNeighbors(enum LightBank bank,
		v3s16 pos, u8 lightwas,
Perttu Ahola's avatar
Perttu Ahola committed
		core::map<v3s16, bool> & light_sources,
		core::map<v3s16, MapBlock*>  & modified_blocks)
{
	core::map<v3s16, u8> from_nodes;
	from_nodes.insert(pos, lightwas);

Perttu Ahola's avatar
Perttu Ahola committed
	unspreadLight(bank, from_nodes, light_sources, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
}

/*
	Lights neighbors of from_nodes, collects all them and then
	goes on recursively.
*/
Perttu Ahola's avatar
Perttu Ahola committed
void Map::spreadLight(enum LightBank bank,
		core::map<v3s16, bool> & from_nodes,
Perttu Ahola's avatar
Perttu Ahola committed
		core::map<v3s16, MapBlock*> & modified_blocks)
{
	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;
	
	u32 blockchangecount = 0;

	core::map<v3s16, bool> lighted_nodes;
	core::map<v3s16, bool>::Iterator j;
	j = from_nodes.getIterator();

	/*
		Initialize block cache
	*/
	v3s16 blockpos_last;
	MapBlock *block = NULL;
	// Cache this a bit, too
	bool block_checked_in_modified = false;
	
	for(; j.atEnd() == false; j++)
	//for(; j != from_nodes.end(); j++)
	{
		v3s16 pos = j.getNode()->getKey();
		//v3s16 pos = *j;
		//dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
		v3s16 blockpos = getNodeBlockPos(pos);
		
		// Only fetch a new block if the block position has changed
		try{
			if(block == NULL || blockpos != blockpos_last){
				block = getBlockNoCreate(blockpos);
				blockpos_last = blockpos;

				block_checked_in_modified = false;
				blockchangecount++;
			}
		}
		catch(InvalidPositionException &e)
		{
			continue;
		}

		if(block->isDummy())
			continue;

		// Calculate relative position in block
		v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;

		// Get node straight from the block
		MapNode n = block->getNode(relpos);

Perttu Ahola's avatar
Perttu Ahola committed
		u8 oldlight = n.getLight(bank);
Perttu Ahola's avatar
Perttu Ahola committed
		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];
			
			// Get the block where the node is located
			v3s16 blockpos = getNodeBlockPos(n2pos);

			try
			{
				// Only fetch a new block if the block position has changed
				try{
					if(block == NULL || blockpos != blockpos_last){
						block = getBlockNoCreate(blockpos);
						blockpos_last = blockpos;

						block_checked_in_modified = false;
						blockchangecount++;
					}
				}
				catch(InvalidPositionException &e)
				{
					continue;
				}
				
				// Calculate relative position in block
				v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
				// Get node straight from the block
				MapNode n2 = block->getNode(relpos);
				
				bool changed = false;
				/*
					If the neighbor is brighter than the current node,
					add to list (it will light up this node on its turn)
				*/
Perttu Ahola's avatar
Perttu Ahola committed
				if(n2.getLight(bank) > undiminish_light(oldlight))
Perttu Ahola's avatar
Perttu Ahola committed
				{
					lighted_nodes.insert(n2pos, true);
					//lighted_nodes.push_back(n2pos);
					changed = true;
				}
				/*
					If the neighbor is dimmer than how much light this node
					would spread on it, add to list
				*/
Perttu Ahola's avatar
Perttu Ahola committed
				if(n2.getLight(bank) < newlight)
Perttu Ahola's avatar
Perttu Ahola committed
				{
					if(n2.light_propagates())
					{
Perttu Ahola's avatar
Perttu Ahola committed
						n2.setLight(bank, newlight);
Perttu Ahola's avatar
Perttu Ahola committed
						block->setNode(relpos, n2);
						lighted_nodes.insert(n2pos, true);
						//lighted_nodes.push_back(n2pos);
						changed = true;
					}
				}

				// Add to modified_blocks
				if(changed == true && block_checked_in_modified == false)
				{
					// If the block is not found in modified_blocks, add.
					if(modified_blocks.find(blockpos) == NULL)
					{
						modified_blocks.insert(blockpos, block);
					}
					block_checked_in_modified = true;
				}
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}
		}
	}

	/*dstream<<"spreadLight(): Changed block "
			<<blockchangecount<<" times"
			<<" for "<<from_nodes.size()<<" nodes"
			<<std::endl;*/
	
	if(lighted_nodes.size() > 0)
Perttu Ahola's avatar
Perttu Ahola committed
		spreadLight(bank, lighted_nodes, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
}

/*
	A single-node source variation of the above.
*/
Perttu Ahola's avatar
Perttu Ahola committed
void Map::lightNeighbors(enum LightBank bank,
		v3s16 pos,
Perttu Ahola's avatar
Perttu Ahola committed
		core::map<v3s16, MapBlock*> & modified_blocks)
{
	core::map<v3s16, bool> from_nodes;
	from_nodes.insert(pos, true);
Perttu Ahola's avatar
Perttu Ahola committed
	spreadLight(bank, from_nodes, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
}

Perttu Ahola's avatar
Perttu Ahola committed
v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
Perttu Ahola's avatar
Perttu Ahola committed
{
	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
	};
	
	u8 brightest_light = 0;
	v3s16 brightest_pos(0,0,0);
	bool found_something = false;

	// Loop through 6 neighbors
	for(u16 i=0; i<6; i++){
		// Get the position of the neighbor node
		v3s16 n2pos = p + dirs[i];
		MapNode n2;
		try{
			n2 = getNode(n2pos);
		}
		catch(InvalidPositionException &e)
		{
			continue;
		}
Perttu Ahola's avatar
Perttu Ahola committed
		if(n2.getLight(bank) > brightest_light || found_something == false){
			brightest_light = n2.getLight(bank);
Perttu Ahola's avatar
Perttu Ahola committed
			brightest_pos = n2pos;
			found_something = true;
		}
	}

	if(found_something == false)
		throw InvalidPositionException();
		
	return brightest_pos;
}

/*
	Propagates sunlight down from a node.
	Starting point gets sunlight.

	Returns the lowest y value of where the sunlight went.

	Mud is turned into grass in where the sunlight stops.
Perttu Ahola's avatar
Perttu Ahola committed
*/
s16 Map::propagateSunlight(v3s16 start,
		core::map<v3s16, MapBlock*> & modified_blocks)
{
	s16 y = start.Y;
	for(; ; y--)
	{
		v3s16 pos(start.X, y, start.Z);
		
		v3s16 blockpos = getNodeBlockPos(pos);
		MapBlock *block;
		try{
			block = getBlockNoCreate(blockpos);
		}
		catch(InvalidPositionException &e)
		{
			break;
		}

		v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
		MapNode n = block->getNode(relpos);

		if(n.sunlight_propagates())
		{
Perttu Ahola's avatar
Perttu Ahola committed
			n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
Perttu Ahola's avatar
Perttu Ahola committed
			block->setNode(relpos, n);

			modified_blocks.insert(blockpos, block);
		}
		else
		{
			// Turn mud into grass
			if(n.d == CONTENT_MUD)
			{
				n.d = CONTENT_GRASS;
				block->setNode(relpos, n);
				modified_blocks.insert(blockpos, block);
			}

			// Sunlight goes no further
Perttu Ahola's avatar
Perttu Ahola committed
			break;
		}
	}
	return y + 1;
}

Perttu Ahola's avatar
Perttu Ahola committed
void Map::updateLighting(enum LightBank bank,
		core::map<v3s16, MapBlock*> & a_blocks,
Perttu Ahola's avatar
Perttu Ahola committed
		core::map<v3s16, MapBlock*> & modified_blocks)
{
	/*m_dout<<DTIME<<"Map::updateLighting(): "
			<<a_blocks.size()<<" blocks."<<std::endl;*/
	
	//TimeTaker timer("updateLighting");
Perttu Ahola's avatar
Perttu Ahola committed
	
	// For debugging
	bool debug=true;

Perttu Ahola's avatar
Perttu Ahola committed
	u32 count_was = modified_blocks.size();

	core::map<v3s16, bool> light_sources;
	
	core::map<v3s16, u8> unlight_from;
		
	core::map<v3s16, MapBlock*>::Iterator i;
	i = a_blocks.getIterator();
	for(; i.atEnd() == false; i++)
	{
		MapBlock *block = i.getNode()->getValue();
		
		for(;;)
		{
			// Don't bother with dummy blocks.
			if(block->isDummy())
				break;
		
			v3s16 pos = block->getPos();
			modified_blocks.insert(pos, block);

			/*
				Clear all light from block
			*/
			for(s16 z=0; z<MAP_BLOCKSIZE; z++)
			for(s16 x=0; x<MAP_BLOCKSIZE; x++)
			for(s16 y=0; y<MAP_BLOCKSIZE; y++)
			{
				
				try{
					v3s16 p(x,y,z);
					MapNode n = block->getNode(v3s16(x,y,z));
Perttu Ahola's avatar
Perttu Ahola committed
					u8 oldlight = n.getLight(bank);
					n.setLight(bank, 0);
Perttu Ahola's avatar
Perttu Ahola committed
					block->setNode(v3s16(x,y,z), n);
					
					// Collect borders for unlighting
					if(x==0 || x == MAP_BLOCKSIZE-1
Perttu Ahola's avatar
Perttu Ahola committed
					|| y==0 || y == MAP_BLOCKSIZE-1
					|| z==0 || z == MAP_BLOCKSIZE-1)
Perttu Ahola's avatar
Perttu Ahola committed
					{
						v3s16 p_map = p + v3s16(
								MAP_BLOCKSIZE*pos.X,
								MAP_BLOCKSIZE*pos.Y,
								MAP_BLOCKSIZE*pos.Z);
						unlight_from.insert(p_map, oldlight);
					}
				}
				catch(InvalidPositionException &e)
				{
					/*
						This would happen when dealing with a
						dummy block.
					*/
					//assert(0);
					dstream<<"updateLighting(): InvalidPositionException"
							<<std::endl;
				}
			}
			
Perttu Ahola's avatar
Perttu Ahola committed
			if(bank == LIGHTBANK_DAY)
			{
				bool bottom_valid = block->propagateSunlight(light_sources);
Perttu Ahola's avatar
Perttu Ahola committed

Perttu Ahola's avatar
Perttu Ahola committed
				// If bottom is valid, we're done.
				if(bottom_valid)
					break;
			}
			else if(bank == LIGHTBANK_NIGHT)
			{
Perttu Ahola's avatar
Perttu Ahola committed
				break;
Perttu Ahola's avatar
Perttu Ahola committed
			}
			else
			{
				assert(0);
			}
Perttu Ahola's avatar
Perttu Ahola committed
				
			/*dstream<<"Bottom for sunlight-propagated block ("
					<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
					<<std::endl;*/

			// Else get the block below and loop to it

			pos.Y--;
			try{
				block = getBlockNoCreate(pos);
			}
			catch(InvalidPositionException &e)
			{
				assert(0);
			}
			
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed
	{
		TimeTaker timer("unspreadLight");
Perttu Ahola's avatar
Perttu Ahola committed
		unspreadLight(bank, unlight_from, light_sources, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
	}
	
	if(debug)
	{
		u32 diff = modified_blocks.size() - count_was;
		count_was = modified_blocks.size();
		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
Perttu Ahola's avatar
Perttu Ahola committed
	//       - Find out why it works
Perttu Ahola's avatar
Perttu Ahola committed

	{
		TimeTaker timer("spreadLight");
Perttu Ahola's avatar
Perttu Ahola committed
		spreadLight(bank, light_sources, modified_blocks);
Perttu Ahola's avatar
Perttu Ahola committed
	}
	
	if(debug)
	{
		u32 diff = modified_blocks.size() - count_was;
		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;*/
	}
Perttu Ahola's avatar
Perttu Ahola committed

	//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
}

Perttu Ahola's avatar
Perttu Ahola committed
void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
		core::map<v3s16, MapBlock*> & modified_blocks)
{
	updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
	updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
	
	/*
		Update information about whether day and night light differ
	*/
	for(core::map<v3s16, MapBlock*>::Iterator
			i = modified_blocks.getIterator();
			i.atEnd() == false; i++)
	{
		MapBlock *block = i.getNode()->getValue();
		block->updateDayNightDiff();
	}
Perttu Ahola's avatar
Perttu Ahola committed
/*
	This is called after changing a node from transparent to opaque.
	The lighting value of the node should be left as-is after changing
	other values. This sets the lighting value to 0.
*/
/*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
		core::map<v3s16, MapBlock*> &modified_blocks)*/
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
		core::map<v3s16, MapBlock*> &modified_blocks)
{
	/*PrintInfo(m_dout);
	m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
			<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/

	/*
		From this node to nodes underneath:
		If lighting is sunlight (1.0), unlight neighbours and
		set lighting to 0.
		Else discontinue.
	*/

	v3s16 toppos = p + v3s16(0,1,0);
Perttu Ahola's avatar
Perttu Ahola committed
	v3s16 bottompos = p + v3s16(0,-1,0);
Perttu Ahola's avatar
Perttu Ahola committed

Perttu Ahola's avatar
Perttu Ahola committed
	bool node_under_sunlight = true;
	core::map<v3s16, bool> light_sources;

Perttu Ahola's avatar
Perttu Ahola committed
	/*
		If there is a node at top and it doesn't have sunlight,
		there has not been any sunlight going down.

		Otherwise there probably is.
	*/
	try{
		MapNode topnode = getNode(toppos);

Perttu Ahola's avatar
Perttu Ahola committed
		if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
Perttu Ahola's avatar
Perttu Ahola committed
			node_under_sunlight = false;
	}
	catch(InvalidPositionException &e)
	{
	}
Perttu Ahola's avatar
Perttu Ahola committed
	
	if(n.d != CONTENT_TORCH)
	{
		/*
			If there is grass below, change it to mud
		*/
		try{
			MapNode bottomnode = getNode(bottompos);
			
			if(bottomnode.d == CONTENT_GRASS
					|| bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
			{
				bottomnode.d = CONTENT_MUD;
				setNode(bottompos, bottomnode);
			}
		}
		catch(InvalidPositionException &e)
		{
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed

Perttu Ahola's avatar
Perttu Ahola committed
	enum LightBank banks[] =
	{
		LIGHTBANK_DAY,
		LIGHTBANK_NIGHT
	};
	for(s32 i=0; i<2; i++)
	{
		enum LightBank bank = banks[i];

		u8 lightwas = getNode(p).getLight(bank);

		// Add the block of the added node to modified_blocks
		v3s16 blockpos = getNodeBlockPos(p);
		MapBlock * block = getBlockNoCreate(blockpos);
		assert(block != NULL);
		modified_blocks.insert(blockpos, block);
Perttu Ahola's avatar
Perttu Ahola committed
		
Perttu Ahola's avatar
Perttu Ahola committed
		if(isValidPosition(p) == false)
			throw;
			
		// Unlight neighbours of node.
		// This means setting light of all consequent dimmer nodes
		// to 0.
		// This also collects the nodes at the border which will spread
		// light again into this.
		unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);

		n.setLight(bank, 0);
	}
	
Perttu Ahola's avatar
Perttu Ahola committed
	setNode(p, n);
	
	/*
		If node is under sunlight, take all sunlighted nodes under
		it and clear light from them and from where the light has
		been spread.
Perttu Ahola's avatar
Perttu Ahola committed
		TODO: This could be optimized by mass-unlighting instead
		      of looping
Perttu Ahola's avatar
Perttu Ahola committed
	*/
	if(node_under_sunlight)
	{
		s16 y = p.Y - 1;
		for(;; y--){
			//m_dout<<DTIME<<"y="<<y<<std::endl;
			v3s16 n2pos(p.X, y, p.Z);
			
			MapNode n2;
			try{
				n2 = getNode(n2pos);
			}
			catch(InvalidPositionException &e)
			{
				break;
			}

Perttu Ahola's avatar
Perttu Ahola committed
			if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
Perttu Ahola's avatar
Perttu Ahola committed
			{
				//m_dout<<DTIME<<"doing"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
				unLightNeighbors(LIGHTBANK_DAY,
						n2pos, n2.getLight(LIGHTBANK_DAY),
						light_sources, modified_blocks);
				n2.setLight(LIGHTBANK_DAY, 0);
Perttu Ahola's avatar
Perttu Ahola committed
				setNode(n2pos, n2);
			}
			else
				break;
		}
	}
	
Perttu Ahola's avatar
Perttu Ahola committed
	for(s32 i=0; i<2; i++)
	{
		enum LightBank bank = banks[i];
		
		/*
			Spread light from all nodes that might be capable of doing so
			TODO: Convert to spreadLight
		*/
		spreadLight(bank, light_sources, modified_blocks);
	}

	/*
		Update information about whether day and night light differ
	*/
	for(core::map<v3s16, MapBlock*>::Iterator
			i = modified_blocks.getIterator();
			i.atEnd() == false; i++)
	{
		MapBlock *block = i.getNode()->getValue();
		block->updateDayNightDiff();
	}

	/*
		Add neighboring liquid nodes and the node itself if it is
		liquid (=water node was added) to transform queue.
	*/
	v3s16 dirs[7] = {
		v3s16(0,0,0), // self
		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
	};
	for(u16 i=0; i<7; i++)
	{
		try
		{

		v3s16 p2 = p + dirs[i];
		
		MapNode n2 = getNode(p2);
		if(content_liquid(n2.d))
		{
			m_transforming_liquid.push_back(p2);
		}
		
		}catch(InvalidPositionException &e)
		{
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed
}

/*
*/
void Map::removeNodeAndUpdate(v3s16 p,
		core::map<v3s16, MapBlock*> &modified_blocks)
{