Example #1
0
	void Run()
	{
		TC parent;

		MapBlock b(&parent, v3s16(1,1,1));
		v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);

		UASSERT(b.getPosRelative() == relpos);

		UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE);
		UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1);
		UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE);
		UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1);
		UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE);
		UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1);

		UASSERT(b.isValidPosition(v3s16(0,0,0)) == true);
		UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false);
		UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false);
		UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false);
		UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
		UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false);

		/*
			TODO: this method should probably be removed
			if the block size isn't going to be set variable
		*/
		/*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE,
				MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/

		// Changed flag should be initially set
		UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED);
		b.resetModified();
		UASSERT(b.getModified() == MOD_STATE_CLEAN);

		// All nodes should have been set to
		// .d=CONTENT_IGNORE and .getLight() = 0
		for(u16 z=0; z<MAP_BLOCKSIZE; z++)
		for(u16 y=0; y<MAP_BLOCKSIZE; y++)
		for(u16 x=0; x<MAP_BLOCKSIZE; x++)
		{
			//UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR);
			UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE);
			UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0);
			UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0);
		}

		{
			MapNode n(CONTENT_AIR);
			for(u16 z=0; z<MAP_BLOCKSIZE; z++)
			for(u16 y=0; y<MAP_BLOCKSIZE; y++)
			for(u16 x=0; x<MAP_BLOCKSIZE; x++)
			{
				b.setNode(v3s16(x,y,z), n);
			}
		}

		/*
			Parent fetch functions
		*/
		parent.position_valid = false;
		parent.node.setContent(5);

		MapNode n;

		// Positions in the block should still be valid
		UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true);
		UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
		n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0));
		UASSERT(n.getContent() == CONTENT_AIR);

		// ...but outside the block they should be invalid
		UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false);
		UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false);
		UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false);

		{
			bool exception_thrown = false;
			try{
				// This should throw an exception
				MapNode n = b.getNodeParent(v3s16(0,0,-1));
			}
			catch(InvalidPositionException &e)
			{
				exception_thrown = true;
			}
			UASSERT(exception_thrown);
		}

		parent.position_valid = true;
		// Now the positions outside should be valid
		UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true);
		UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true);
		UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true);
		n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE));
		UASSERT(n.getContent() == 5);

		/*
			Set a node
		*/
		v3s16 p(1,2,0);
		n.setContent(4);
		b.setNode(p, n);
		UASSERT(b.getNode(p).getContent() == 4);
		//TODO: Update to new system
		/*UASSERT(b.getNodeTile(p) == 4);
		UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/

		/*
			propagateSunlight()
		*/
		// Set lighting of all nodes to 0
		for(u16 z=0; z<MAP_BLOCKSIZE; z++){
			for(u16 y=0; y<MAP_BLOCKSIZE; y++){
				for(u16 x=0; x<MAP_BLOCKSIZE; x++){
					MapNode n = b.getNode(v3s16(x,y,z));
					n.setLight(LIGHTBANK_DAY, 0);
					n.setLight(LIGHTBANK_NIGHT, 0);
					b.setNode(v3s16(x,y,z), n);
				}
			}
		}
		{
			/*
				Check how the block handles being a lonely sky block
			*/
			parent.position_valid = true;
			b.setIsUnderground(false);
			parent.node.setContent(CONTENT_AIR);
			parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN);
			parent.node.setLight(LIGHTBANK_NIGHT, 0);
			core::map<v3s16, bool> light_sources;
			// The bottom block is invalid, because we have a shadowing node
			UASSERT(b.propagateSunlight(light_sources) == false);
			UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
			UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
			UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0);
			UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0);
			UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0);
			UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
			UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN);
			UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0);
			UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0);
			// According to MapBlock::getFaceLight,
			// The face on the z+ side should have double-diminished light
			//UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX)));
			// The face on the z+ side should have diminished light
			UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX));
		}
		/*
			Check how the block handles being in between blocks with some non-sunlight
			while being underground
		*/
		{
			// Make neighbours to exist and set some non-sunlight to them
			parent.position_valid = true;
			b.setIsUnderground(true);
			parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
			core::map<v3s16, bool> light_sources;
			// The block below should be valid because there shouldn't be
			// sunlight in there either
			UASSERT(b.propagateSunlight(light_sources, true) == true);
			// Should not touch nodes that are not affected (that is, all of them)
			//UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN);
			// Should set light of non-sunlighted blocks to 0.
			UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0);
		}
		/*
			Set up a situation where:
			- There is only air in this block
			- There is a valid non-sunlighted block at the bottom, and
			- Invalid blocks elsewhere.
			- the block is not underground.

			This should result in bottom block invalidity
		*/
		{
			b.setIsUnderground(false);
			// Clear block
			for(u16 z=0; z<MAP_BLOCKSIZE; z++){
				for(u16 y=0; y<MAP_BLOCKSIZE; y++){
					for(u16 x=0; x<MAP_BLOCKSIZE; x++){
						MapNode n;
						n.setContent(CONTENT_AIR);
						n.setLight(LIGHTBANK_DAY, 0);
						b.setNode(v3s16(x,y,z), n);
					}
				}
			}
			// Make neighbours invalid
			parent.position_valid = false;
			// Add exceptions to the top of the bottom block
			for(u16 x=0; x<MAP_BLOCKSIZE; x++)
			for(u16 z=0; z<MAP_BLOCKSIZE; z++)
			{
				parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z));
			}
			// Lighting value for the valid nodes
			parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
			core::map<v3s16, bool> light_sources;
			// Bottom block is not valid
			UASSERT(b.propagateSunlight(light_sources) == false);
		}
	}
Example #2
0
bool Map::propagateSunlight(v3POS pos, std::set<v3POS> & light_sources,
                            bool remove_light) {
	MapBlock *block = getBlockNoCreateNoEx(pos);

	INodeDefManager *nodemgr = m_gamedef->ndef();

	// Whether the sunlight at the top of the bottom block is valid
	bool block_below_is_valid = true;

	v3POS pos_relative = block->getPosRelative();

	for(s16 x = 0; x < MAP_BLOCKSIZE; ++x) {
		for(s16 z = 0; z < MAP_BLOCKSIZE; ++z) {
			bool no_sunlight = false;

			// Check if node above block has sunlight

			MapNode n = getNode(pos_relative + v3POS(x, MAP_BLOCKSIZE, z));
			if (n) {
				if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) {
					no_sunlight = true;
				}
			} else {

				// NOTE: This makes over-ground roofed places sunlighted
				// Assume sunlight, unless is_underground==true
				if(block->getIsUnderground()) {
					no_sunlight = true;
				} else {
					MapNode n = block->getNode(v3POS(x, MAP_BLOCKSIZE - 1, z));
					if(n && m_gamedef->ndef()->get(n).sunlight_propagates == false)
						no_sunlight = true;
				}
				// NOTE: As of now, this just would make everything dark.
				// No sunlight here
				//no_sunlight = true;
			}

			s16 y = MAP_BLOCKSIZE - 1;

			// This makes difference to diminishing in water.
			//bool stopped_to_solid_object = false;

			u8 current_light = no_sunlight ? 0 : LIGHT_SUN;

			for(; y >= 0; --y) {
				v3POS pos(x, y, z);
				MapNode n = block->getNode(pos);

				if(current_light == 0) {
					// Do nothing
				} else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) {
					// Do nothing: Sunlight is continued
				} else if(nodemgr->get(n).light_propagates == false) {
					// A solid object is on the way.
					//stopped_to_solid_object = true;

					// Light stops.
					current_light = 0;
				} else {
					// Diminish light
					current_light = diminish_light(current_light);
				}

				u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);

				if(current_light > old_light || remove_light) {
					n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
					block->setNode(pos, n);
				}

				if(diminish_light(current_light) != 0) {
					light_sources.insert(pos_relative + pos);
				}

			}

			// Whether or not the block below should see LIGHT_SUN
			bool sunlight_should_go_down = (current_light == LIGHT_SUN);

			/*
				If the block below hasn't already been marked invalid:

				Check if the node below the block has proper sunlight at top.
				If not, the block below is invalid.

				Ignore non-transparent nodes as they always have no light
			*/

			if(block_below_is_valid) {
				MapNode n = getNode(pos_relative + v3POS(x, -1, z));
				if (n) {
					if(nodemgr->get(n).light_propagates) {
						if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
						        && sunlight_should_go_down == false)
							block_below_is_valid = false;
						else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
						        && sunlight_should_go_down == true)
							block_below_is_valid = false;
					}
				} else {
					// Just no block below, no need to panic.
				}
			}
		}
	}

	return block_below_is_valid;
}
Example #3
0
/*
	Propagates sunlight down through the block.
	Doesn't modify nodes that are not affected by sunlight.

	Returns false if sunlight at bottom block is invalid.
	Returns true if sunlight at bottom block is valid.
	Returns true if bottom block doesn't exist.

	If there is a block above, continues from it.
	If there is no block above, assumes there is sunlight, unless
	is_underground is set or highest node is water.

	All sunlighted nodes are added to light_sources.

	if remove_light==true, sets non-sunlighted nodes black.

	if black_air_left!=NULL, it is set to true if non-sunlighted
	air is left in block.
*/
bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
		bool remove_light, bool *black_air_left)
{
	auto lock = lock_unique_rec();

	INodeDefManager *nodemgr = m_gamedef->ndef();

	// Whether the sunlight at the top of the bottom block is valid
	bool block_below_is_valid = true;

	v3s16 pos_relative = getPosRelative();

	for(s16 x=0; x<MAP_BLOCKSIZE; x++)
	{
		for(s16 z=0; z<MAP_BLOCKSIZE; z++)
		{
#if 1
			bool no_sunlight = false;
			//bool no_top_block = false;

			// Check if node above block has sunlight

			bool is_valid_position;
			MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
				&is_valid_position);
			if (n)
			{
				if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
				{
					no_sunlight = true;
				}
			}
			else
			{
				//no_top_block = true;

				// NOTE: This makes over-ground roofed places sunlighted
				// Assume sunlight, unless is_underground==true
				if(is_underground)
				{
					no_sunlight = true;
				}
				else
				{
					MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
					if(n && m_gamedef->ndef()->get(n).sunlight_propagates == false)
					{
						no_sunlight = true;
					}
				}
				// NOTE: As of now, this just would make everything dark.
				// No sunlight here
				//no_sunlight = true;
			}
#endif
#if 0 // Doesn't work; nothing gets light.
			bool no_sunlight = true;
			bool no_top_block = false;
			// Check if node above block has sunlight
			try{
				MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
				if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
				{
					no_sunlight = false;
				}
			}
			catch(InvalidPositionException &e)
			{
				no_top_block = true;
			}
#endif

			/*std::cout<<"("<<x<<","<<z<<"): "
					<<"no_top_block="<<no_top_block
					<<", is_underground="<<is_underground
					<<", no_sunlight="<<no_sunlight
					<<std::endl;*/

			s16 y = MAP_BLOCKSIZE-1;

			// This makes difference to diminishing in water.
			bool stopped_to_solid_object = false;

			u8 current_light = no_sunlight ? 0 : LIGHT_SUN;

			for(; y >= 0; y--)
			{
				v3s16 pos(x, y, z);
				MapNode &n = getNodeRef(pos);

				if(current_light == 0)
				{
					// Do nothing
				}
				else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
				{
					// Do nothing: Sunlight is continued
				}
				else if(nodemgr->get(n).light_propagates == false)
				{
					// A solid object is on the way.
					stopped_to_solid_object = true;

					// Light stops.
					current_light = 0;
				}
				else
				{
					// Diminish light
					current_light = diminish_light(current_light);
				}

				u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);

				if(current_light > old_light || remove_light)
				{
					n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
				}

				if(diminish_light(current_light) != 0)
				{
					light_sources.insert(pos_relative + pos);
				}

				if(current_light == 0 && stopped_to_solid_object)
				{
					if(black_air_left)
					{
						*black_air_left = true;
					}
				}
			}

			// Whether or not the block below should see LIGHT_SUN
			bool sunlight_should_go_down = (current_light == LIGHT_SUN);

			/*
				If the block below hasn't already been marked invalid:

				Check if the node below the block has proper sunlight at top.
				If not, the block below is invalid.

				Ignore non-transparent nodes as they always have no light
			*/

			if(block_below_is_valid)
			{
				MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
				if (n) {
					if(nodemgr->get(n).light_propagates)
					{
						if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
								&& sunlight_should_go_down == false)
							block_below_is_valid = false;
						else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
								&& sunlight_should_go_down == true)
							block_below_is_valid = false;
					}
				}
				else
				{
					/*std::cout<<"InvalidBlockException for bottom block node"
							<<std::endl;*/
					// Just no block below, no need to panic.
				}
			}
		}
	}

	return block_below_is_valid;
}
Example #4
0
u32 Map::updateLighting(concurrent_map<v3POS, MapBlock*> & a_blocks,
                        std::map<v3POS, MapBlock*> & modified_blocks, unsigned int max_cycle_ms) {
	INodeDefManager *nodemgr = m_gamedef->ndef();

	int ret = 0;
	int loopcount = 0;

	TimeTaker timer("updateLighting");

	// For debugging
	//bool debug=true;
	//u32 count_was = modified_blocks.size();

	//std::unordered_set<v3POS, v3POSHash, v3POSEqual> light_sources;
	//std::unordered_map<v3POS, u8, v3POSHash, v3POSEqual> unlight_from_day, unlight_from_night;
	std::set<v3POS> light_sources;
	std::map<v3POS, u8> unlight_from_day, unlight_from_night;
	unordered_map_v3POS<int> processed;


	int num_bottom_invalid = 0;

	//MutexAutoLock lock2(m_update_lighting_mutex);

#if !ENABLE_THREADS
	auto lock = m_nothread_locker.lock_unique_rec();
#endif

	{
		//TimeTaker t("updateLighting: first stuff");

		u32 end_ms = porting::getTimeMs() + max_cycle_ms;
		for(auto i = a_blocks.begin();
		        i != a_blocks.end(); ++i) {

			auto block = getBlockNoCreateNoEx(i->first);

			for(;;) {
				// Don't bother with dummy blocks.
				if(!block || block->isDummy())
					break;

				auto lock = block->try_lock_unique_rec();
				if (!lock->owns_lock())
					break; // may cause dark areas
				v3POS pos = block->getPos();
				if (processed.count(pos) && processed[pos] <= i->first.Y ) {
					break;
				}
				++loopcount;
				processed[pos] = i->first.Y;
				v3POS posnodes = block->getPosRelative();
				//modified_blocks[pos] = block;

				block->setLightingExpired(true);
				block->lighting_broken = true;

				/*
					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++) {
							v3POS p(x, y, z);
							bool is_valid_position;
							MapNode n = block->getNode(p, &is_valid_position);
							if (!is_valid_position) {
								/* This would happen when dealing with a
								   dummy block.
								*/
								infostream << "updateLighting(): InvalidPositionException"
								           << std::endl;
								continue;
							}
							u8 oldlight_day = n.getLight(LIGHTBANK_DAY, nodemgr);
							u8 oldlight_night = n.getLight(LIGHTBANK_NIGHT, nodemgr);
							n.setLight(LIGHTBANK_DAY, 0, nodemgr);
							n.setLight(LIGHTBANK_NIGHT, 0, nodemgr);
							block->setNode(p, n);

							// If node sources light, add to list
							//u8 source = nodemgr->get(n).light_source;
							if(nodemgr->get(n).light_source)
								light_sources.insert(p + posnodes);

							v3POS p_map = p + posnodes;
							// Collect borders for unlighting
							if(x == 0 || x == MAP_BLOCKSIZE - 1
							        || y == 0 || y == MAP_BLOCKSIZE - 1
							        || z == 0 || z == MAP_BLOCKSIZE - 1) {
								if(oldlight_day)
									unlight_from_day[p_map] = oldlight_day;
								if(oldlight_night)
									unlight_from_night[p_map] = oldlight_night;
							}


						}

				lock->unlock();

				bool bottom_valid = propagateSunlight(pos, light_sources);

				if(!bottom_valid)
					num_bottom_invalid++;

				pos.Y--;
				block = getBlockNoCreateNoEx(pos);
			}

			if (porting::getTimeMs() > end_ms) {
				++ret;
				break;
			}
		}

	}

	{
		//TimeTaker timer("updateLighting: unspreadLight");
		unspreadLight(LIGHTBANK_DAY, unlight_from_day, light_sources, modified_blocks);
		unspreadLight(LIGHTBANK_NIGHT, unlight_from_night, light_sources, modified_blocks);
	}

	{
		//TimeTaker timer("updateLighting: spreadLight");
		spreadLight(LIGHTBANK_DAY, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10);
		spreadLight(LIGHTBANK_NIGHT, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10);
	}

	for (auto & i : processed) {
		a_blocks.erase(i.first);
		MapBlock *block = getBlockNoCreateNoEx(i.first);
		if(!block)
			continue;
		block->setLightingExpired(false);
		block->lighting_broken = false;
	}

	g_profiler->add("Server: light blocks", loopcount);

	return ret;

}