Skip to content
Snippets Groups Projects
map.cpp 101 KiB
Newer Older
Perttu Ahola's avatar
Perttu Ahola committed
/*
Minetest
sfan5's avatar
sfan5 committed
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Perttu Ahola's avatar
Perttu Ahola committed

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
Perttu Ahola's avatar
Perttu Ahola committed
(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 Lesser General Public License for more details.
Perttu Ahola's avatar
Perttu Ahola committed

You should have received a copy of the GNU Lesser General Public License along
Perttu Ahola's avatar
Perttu Ahola committed
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"
Perttu Ahola's avatar
Perttu Ahola committed
#include "mapsector.h"
#include "mapblock.h"
Perttu Ahola's avatar
Perttu Ahola committed
#include "main.h"
#include "filesys.h"
#include "voxel.h"
Perttu Ahola's avatar
Perttu Ahola committed
#include "porting.h"
Perttu Ahola's avatar
Perttu Ahola committed
#include "nodemetadata.h"
#include "settings.h"
#include "profiler.h"
Perttu Ahola's avatar
Perttu Ahola committed
#include "nodedef.h"
#include "gamedef.h"
#include "util/directiontables.h"
proller's avatar
proller committed
#include "util/mathconstants.h"
#include "rollback_interface.h"
#include "emerge.h"
#include "mapgen_v6.h"
proller's avatar
proller committed
#include "biome.h"
Kahrl's avatar
Kahrl committed
#include "config.h"
#include "server.h"
#include "database.h"
#include "database-dummy.h"
#include "database-sqlite3.h"
#if USE_LEVELDB
#include "database-leveldb.h"
#endif
Perttu Ahola's avatar
Perttu Ahola committed

#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"

/*
	SQLite format specification:
	- Initially only replaces sectors/ and sectors2/
JacobF's avatar
JacobF committed
	If map.sqlite does not exist in the save dir
	or the block was not found in the database
	the map will try to load from sectors folder.
	In either case, map.sqlite will be created
	and all future saves will save there.
JacobF's avatar
JacobF committed
	Structure of map.sqlite:
	Tables:
		blocks
			(PK) INT pos
			BLOB data
Map::Map(std::ostream &dout, IGameDef *gamedef):
Perttu Ahola's avatar
Perttu Ahola committed
	m_dout(dout),
Perttu Ahola's avatar
Perttu Ahola committed
	m_sector_cache(NULL)
Perttu Ahola's avatar
Perttu Ahola committed
{
	/*m_sector_mutex.Init();
	assert(m_sector_mutex.IsInitialized());*/
Perttu Ahola's avatar
Perttu Ahola committed
}

Map::~Map()
{
	/*
		Free all MapSectors
Perttu Ahola's avatar
Perttu Ahola committed
	*/
	for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
		i != m_sectors.end(); ++i)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		delete i->second;
Perttu Ahola's avatar
Perttu Ahola committed
	}
}

void Map::addEventReceiver(MapEventReceiver *event_receiver)
{
	m_event_receivers.insert(event_receiver);
}

void Map::removeEventReceiver(MapEventReceiver *event_receiver)
{
	m_event_receivers.erase(event_receiver);
}

void Map::dispatchEvent(MapEditEvent *event)
{
	for(std::set<MapEventReceiver*>::iterator
			i = m_event_receivers.begin();
			i != m_event_receivers.end(); ++i)
		(*i)->onMapEditEvent(event);
MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
Perttu Ahola's avatar
Perttu Ahola committed
{
	if(m_sector_cache != NULL && p == m_sector_cache_p){
		MapSector * sector = m_sector_cache;
		return sector;
	}
	std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
	if(n == m_sectors.end())
	MapSector *sector = n->second;
Perttu Ahola's avatar
Perttu Ahola committed
	// Cache the last result
	m_sector_cache_p = p;
	m_sector_cache = sector;

	return sector;
}

MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
{
	return getSectorNoGenerateNoExNoLock(p);
}

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

Perttu Ahola's avatar
Perttu Ahola committed
bool Map::isValidPosition(v3s16 p)
{
	v3s16 blockpos = getNodeBlockPos(p);
	MapBlock *block = getBlockNoCreate(blockpos);
	return (block != NULL);
}

// Returns a CONTENT_IGNORE node if not found
MapNode Map::getNodeNoEx(v3s16 p)
{
	v3s16 blockpos = getNodeBlockPos(p);
	MapBlock *block = getBlockNoCreateNoEx(blockpos);
	if(block == NULL)
		return MapNode(CONTENT_IGNORE);
	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
	return block->getNodeNoCheck(relpos);
}

// throws InvalidPositionException if not found
MapNode Map::getNode(v3s16 p)
{
	v3s16 blockpos = getNodeBlockPos(p);
	MapBlock *block = getBlockNoCreateNoEx(blockpos);
	if(block == NULL)
		throw InvalidPositionException();
	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
	return block->getNodeNoCheck(relpos);
}

// throws InvalidPositionException if not found
void Map::setNode(v3s16 p, MapNode & n)
{
	v3s16 blockpos = getNodeBlockPos(p);
	MapBlock *block = getBlockNoCreate(blockpos);
	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
	// Never allow placing CONTENT_IGNORE, it fucks up stuff
	if(n.getContent() == CONTENT_IGNORE){
		errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
				<<" while trying to replace \""
				<<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
				<<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
	block->setNodeNoCheck(relpos, n);
}


Perttu Ahola's avatar
Perttu Ahola committed
/*
	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,
		std::map<v3s16, u8> & from_nodes,
		std::set<v3s16> & light_sources,
		std::map<v3s16, MapBlock*>  & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
Perttu Ahola's avatar
Perttu Ahola committed
	INodeDefManager *nodemgr = m_gamedef->ndef();

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
	};
Perttu Ahola's avatar
Perttu Ahola committed
	if(from_nodes.size() == 0)
		return;
Perttu Ahola's avatar
Perttu Ahola committed
	u32 blockchangecount = 0;

	std::map<v3s16, u8> unlighted_nodes;
Perttu Ahola's avatar
Perttu Ahola committed

	/*
		Initialize block cache
	*/
	v3s16 blockpos_last;
	MapBlock *block = NULL;
	// Cache this a bit, too
	bool block_checked_in_modified = false;
	for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
		j != from_nodes.end(); ++j)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		v3s16 pos = j->first;
Perttu Ahola's avatar
Perttu Ahola committed
		v3s16 blockpos = getNodeBlockPos(pos);
Perttu Ahola's avatar
Perttu Ahola committed
		// 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
kwolekr's avatar
kwolekr committed
		//v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
Perttu Ahola's avatar
Perttu Ahola committed

		// Get node straight from the block
kwolekr's avatar
kwolekr committed
		//MapNode n = block->getNode(relpos);
		u8 oldlight = j->second;
Perttu Ahola's avatar
Perttu Ahola committed
		// Loop through 6 neighbors
		for(u16 i=0; i<6; i++)
		{
			// Get the position of the neighbor node
			v3s16 n2pos = pos + dirs[i];
Perttu Ahola's avatar
Perttu Ahola committed
			// 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;
				}
Perttu Ahola's avatar
Perttu Ahola committed
				// Calculate relative position in block
				v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
				// Get node straight from the block
				MapNode n2 = block->getNode(relpos);
Perttu Ahola's avatar
Perttu Ahola committed
				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, nodemgr) < 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(nodemgr->get(n2).light_propagates
							&& n2.getLight(bank, nodemgr) != 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, nodemgr);
						n2.setLight(bank, 0, nodemgr);
Perttu Ahola's avatar
Perttu Ahola committed
						block->setNode(relpos, n2);

						unlighted_nodes[n2pos] = current_light;
Perttu Ahola's avatar
Perttu Ahola committed
						changed = true;

						/*
							Remove from light_sources if it is there
							NOTE: This doesn't happen nearly at all
						*/
						/*if(light_sources.find(n2pos))
						{
							infostream<<"Removed from light_sources"<<std::endl;
Perttu Ahola's avatar
Perttu Ahola committed
							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);
Perttu Ahola's avatar
Perttu Ahola committed
				}

				// 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) == modified_blocks.end())
Perttu Ahola's avatar
Perttu Ahola committed
					{
						modified_blocks[blockpos] = block;
Perttu Ahola's avatar
Perttu Ahola committed
					}
					block_checked_in_modified = true;
				}
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}
		}
	}

	/*infostream<<"unspreadLight(): Changed block "
Perttu Ahola's avatar
Perttu Ahola committed
			<<blockchangecount<<" times"
			<<" for "<<from_nodes.size()<<" nodes"
			<<std::endl;*/
Perttu Ahola's avatar
Perttu Ahola committed
	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,
		std::set<v3s16> & light_sources,
		std::map<v3s16, MapBlock*>  & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
	std::map<v3s16, u8> from_nodes;
	from_nodes[pos] = lightwas;
Perttu Ahola's avatar
Perttu Ahola committed

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,
		std::set<v3s16> & from_nodes,
		std::map<v3s16, MapBlock*> & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
Perttu Ahola's avatar
Perttu Ahola committed
	INodeDefManager *nodemgr = m_gamedef->ndef();

Perttu Ahola's avatar
Perttu Ahola committed
	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;
Perttu Ahola's avatar
Perttu Ahola committed
	u32 blockchangecount = 0;

	std::set<v3s16> lighted_nodes;
Perttu Ahola's avatar
Perttu Ahola committed

	/*
		Initialize block cache
	*/
	v3s16 blockpos_last;
	MapBlock *block = NULL;
	// Cache this a bit, too
	bool block_checked_in_modified = false;
	for(std::set<v3s16>::iterator j = from_nodes.begin();
		j != from_nodes.end(); ++j)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		v3s16 pos = *j;
Perttu Ahola's avatar
Perttu Ahola committed
		v3s16 blockpos = getNodeBlockPos(pos);
Perttu Ahola's avatar
Perttu Ahola committed
		// 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, nodemgr);
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];
Perttu Ahola's avatar
Perttu Ahola committed
			// 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;
				}
Perttu Ahola's avatar
Perttu Ahola committed
				// Calculate relative position in block
				v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
				// Get node straight from the block
				MapNode n2 = block->getNode(relpos);
Perttu Ahola's avatar
Perttu Ahola committed
				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, nodemgr) > undiminish_light(oldlight))
Perttu Ahola's avatar
Perttu Ahola committed
				{
					lighted_nodes.insert(n2pos);
Perttu Ahola's avatar
Perttu Ahola committed
					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, nodemgr) < newlight)
Perttu Ahola's avatar
Perttu Ahola committed
				{
Perttu Ahola's avatar
Perttu Ahola committed
					if(nodemgr->get(n2).light_propagates)
Perttu Ahola's avatar
Perttu Ahola committed
					{
Perttu Ahola's avatar
Perttu Ahola committed
						n2.setLight(bank, newlight, nodemgr);
Perttu Ahola's avatar
Perttu Ahola committed
						block->setNode(relpos, n2);
						lighted_nodes.insert(n2pos);
Perttu Ahola's avatar
Perttu Ahola committed
						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) == modified_blocks.end())
Perttu Ahola's avatar
Perttu Ahola committed
					{
						modified_blocks[blockpos] = block;
Perttu Ahola's avatar
Perttu Ahola committed
					}
					block_checked_in_modified = true;
				}
			}
			catch(InvalidPositionException &e)
			{
				continue;
			}
		}
	}

	/*infostream<<"spreadLight(): Changed block "
Perttu Ahola's avatar
Perttu Ahola committed
			<<blockchangecount<<" times"
			<<" for "<<from_nodes.size()<<" nodes"
			<<std::endl;*/
Perttu Ahola's avatar
Perttu Ahola committed
	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,
		std::map<v3s16, MapBlock*> & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
	std::set<v3s16> from_nodes;
	from_nodes.insert(pos);
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
{
Perttu Ahola's avatar
Perttu Ahola committed
	INodeDefManager *nodemgr = m_gamedef->ndef();

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
	};
Perttu Ahola's avatar
Perttu Ahola committed
	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, nodemgr) > brightest_light || found_something == false){
			brightest_light = n2.getLight(bank, nodemgr);
Perttu Ahola's avatar
Perttu Ahola committed
			brightest_pos = n2pos;
			found_something = true;
		}
	}

	if(found_something == false)
		throw InvalidPositionException();
Perttu Ahola's avatar
Perttu Ahola committed
	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,
		std::map<v3s16, MapBlock*> & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
Perttu Ahola's avatar
Perttu Ahola committed
	INodeDefManager *nodemgr = m_gamedef->ndef();

Perttu Ahola's avatar
Perttu Ahola committed
	s16 y = start.Y;
	for(; ; y--)
	{
		v3s16 pos(start.X, y, start.Z);
Perttu Ahola's avatar
Perttu Ahola committed
		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);

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

			modified_blocks[blockpos] = block;
Perttu Ahola's avatar
Perttu Ahola committed
		}
Perttu Ahola's avatar
Perttu Ahola committed
			break;
		}
	}
	return y + 1;
}

Perttu Ahola's avatar
Perttu Ahola committed
void Map::updateLighting(enum LightBank bank,
		std::map<v3s16, MapBlock*> & a_blocks,
		std::map<v3s16, MapBlock*> & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
Perttu Ahola's avatar
Perttu Ahola committed
	INodeDefManager *nodemgr = m_gamedef->ndef();

Perttu Ahola's avatar
Perttu Ahola committed
	/*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;
	//u32 count_was = modified_blocks.size();
	std::map<v3s16, MapBlock*> blocks_to_update;
Perttu Ahola's avatar
Perttu Ahola committed

	std::set<v3s16> light_sources;
	std::map<v3s16, u8> unlight_from;

	int num_bottom_invalid = 0;
Perttu Ahola's avatar
Perttu Ahola committed
	//TimeTaker t("first stuff");
	for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
		i != a_blocks.end(); ++i)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		MapBlock *block = i->second;
Perttu Ahola's avatar
Perttu Ahola committed
		for(;;)
		{
			// Don't bother with dummy blocks.
			if(block->isDummy())
				break;
Perttu Ahola's avatar
Perttu Ahola committed
			v3s16 pos = block->getPos();
			v3s16 posnodes = block->getPosRelative();
			modified_blocks[pos] = block;
			blocks_to_update[pos] = block;
Perttu Ahola's avatar
Perttu Ahola committed
			/*
				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++)
			{
Perttu Ahola's avatar
Perttu Ahola committed
				try{
					v3s16 p(x,y,z);
					MapNode n = block->getNode(p);
Perttu Ahola's avatar
Perttu Ahola committed
					u8 oldlight = n.getLight(bank, nodemgr);
					n.setLight(bank, 0, nodemgr);
					block->setNode(p, n);
					// If node sources light, add to list
					u8 source = nodemgr->get(n).light_source;
					if(source != 0)
						light_sources.insert(p + posnodes);
Perttu Ahola's avatar
Perttu Ahola committed
					// 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)
					&& oldlight != 0)
Perttu Ahola's avatar
Perttu Ahola committed
					{
						v3s16 p_map = p + posnodes;
						unlight_from[p_map] = oldlight;
Perttu Ahola's avatar
Perttu Ahola committed
					}
				}
				catch(InvalidPositionException &e)
				{
					/*
						This would happen when dealing with a
						dummy block.
					*/
					//assert(0);
					infostream<<"updateLighting(): InvalidPositionException"
Perttu Ahola's avatar
Perttu Ahola committed
							<<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

				if(!bottom_valid)
					num_bottom_invalid++;

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

			// Bottom sunlight is not valid; get the block and loop to it
Perttu Ahola's avatar
Perttu Ahola committed

			pos.Y--;
			try{
				block = getBlockNoCreate(pos);
			}
			catch(InvalidPositionException &e)
			{
				assert(0);
			}
Perttu Ahola's avatar
Perttu Ahola committed
		}
	}
Perttu Ahola's avatar
Perttu Ahola committed
	/*
		Enable this to disable proper lighting for speeding up map
		generation for testing or whatever
	*/
#if 0
	//if(g_settings->get(""))
Perttu Ahola's avatar
Perttu Ahola committed
	{
		core::map<v3s16, MapBlock*>::Iterator i;
		i = blocks_to_update.getIterator();
		for(; i.atEnd() == false; i++)
		{
			MapBlock *block = i.getNode()->getValue();
			v3s16 p = block->getPos();
			block->setLightingExpired(false);
		}
		return;
	}
#endif
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)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		u32 diff = modified_blocks.size() - count_was;
		count_was = modified_blocks.size();
		infostream<<"unspreadLight modified "<<diff<<std::endl;
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)
Perttu Ahola's avatar
Perttu Ahola committed
	{
		u32 diff = modified_blocks.size() - count_was;
		count_was = modified_blocks.size();
		infostream<<"spreadLight modified "<<diff<<std::endl;
	{
		//MapVoxelManipulator vmanip(this);
		// Make a manual voxel manipulator and load all the blocks
		// that touch the requested blocks
		ManualMapVoxelManipulator vmanip(this);

		{
		//TimeTaker timer("initialEmerge");

		core::map<v3s16, MapBlock*>::Iterator i;
		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));

			/*
				Add all surrounding blocks that have up-to-date lighting
				NOTE: This doesn't quite do the job (not everything
			*/
			/*for(s16 z=-1; z<=1; z++)
			for(s16 y=-1; y<=1; y++)
			for(s16 x=-1; x<=1; x++)
			{
				v3s16 p2 = p + v3s16(x,y,z);
				MapBlock *block = getBlockNoCreateNoEx(p2);
				if(block == NULL)
					continue;
				if(block->isDummy())
					continue;
				if(block->getLightingExpired())
					continue;
			// Lighting of block will be updated completely
			block->setLightingExpired(false);
			//TimeTaker timer("unSpreadLight");
Perttu Ahola's avatar
Perttu Ahola committed
			vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
			//TimeTaker timer("spreadLight");
Perttu Ahola's avatar
Perttu Ahola committed
			vmanip.spreadLight(bank, light_sources, nodemgr);
			//TimeTaker timer("blitBack");
			vmanip.blitBack(modified_blocks);
		}
		/*infostream<<"emerge_time="<<emerge_time<<std::endl;
		emerge_time = 0;*/
	}
Perttu Ahola's avatar
Perttu Ahola committed

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

void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
		std::map<v3s16, MapBlock*> & modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
	updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
	updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
	/*
		Update information about whether day and night light differ
	*/
	for(std::map<v3s16, MapBlock*>::iterator
			i = modified_blocks.begin();
			i != modified_blocks.end(); ++i)
		MapBlock *block = i->second;
Perttu Ahola's avatar
Perttu Ahola committed
/*
*/
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
		std::map<v3s16, MapBlock*> &modified_blocks)
Perttu Ahola's avatar
Perttu Ahola committed
{
	INodeDefManager *ndef = m_gamedef->ndef();
Perttu Ahola's avatar
Perttu Ahola committed

Perttu Ahola's avatar
Perttu Ahola committed
	/*PrintInfo(m_dout);
	m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
Perttu Ahola's avatar
Perttu Ahola committed
			<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
Perttu Ahola's avatar
Perttu Ahola committed
	/*
		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);
kwolekr's avatar
kwolekr 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;
	std::set<v3s16> light_sources;
	/*
		Collect old node for rollback
	*/
	RollbackNode rollback_oldnode(this, p, m_gamedef);
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);

		if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
Perttu Ahola's avatar
Perttu Ahola committed
			node_under_sunlight = false;
	}
	catch(InvalidPositionException &e)
	{
	}
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, ndef);
Perttu Ahola's avatar
Perttu Ahola committed

		// Add the block of the added node to modified_blocks
		v3s16 blockpos = getNodeBlockPos(p);
		MapBlock * block = getBlockNoCreate(blockpos);
		assert(block != NULL);
		modified_blocks[blockpos] = block;
		assert(isValidPosition(p));
Perttu Ahola's avatar
Perttu Ahola committed
		// Unlight neighbours of node.
		// This means setting light of all consequent dimmer nodes