1 /*
2 map.cpp
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "map.h"
24 #include "mapblock.h"
25 #ifndef SERVER
26 	#include "mapblock_mesh.h"
27 #endif
28 #include "main.h"
29 #include "filesys.h"
30 #include "voxel.h"
31 #include "porting.h"
32 #include "serialization.h"
33 #include "nodemetadata.h"
34 #include "settings.h"
35 #include "log_types.h"
36 #include "profiler.h"
37 #include "nodedef.h"
38 #include "gamedef.h"
39 #include "util/directiontables.h"
40 #include "util/mathconstants.h"
41 #include "rollback_interface.h"
42 #include "environment.h"
43 #include "emerge.h"
44 #include "mapgen_v6.h"
45 #include "mg_biome.h"
46 #include "config.h"
47 #include "server.h"
48 #include "database.h"
49 #include "database-dummy.h"
50 #include "database-sqlite3.h"
51 #include "circuit.h"
52 #include "scripting_game.h"
53 #if USE_LEVELDB
54 #include "database-leveldb.h"
55 #endif
56 #if USE_REDIS
57 #include "database-redis.h"
58 #endif
59 
60 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
61 
62 /*
63 	SQLite format specification:
64 	- Initially only replaces sectors/ and sectors2/
65 
66 	If map.sqlite does not exist in the save dir
67 	or the block was not found in the database
68 	the map will try to load from sectors folder.
69 	In either case, map.sqlite will be created
70 	and all future saves will save there.
71 
72 	Structure of map.sqlite:
73 	Tables:
74 		blocks
75 			(PK) INT pos
76 			BLOB data
77 */
78 
79 /*
80 	Map
81 */
Map(IGameDef * gamedef,Circuit * circuit)82 Map::Map(IGameDef *gamedef, Circuit* circuit):
83 	m_liquid_step_flow(1000),
84 	m_blocks_delete(&m_blocks_delete_1),
85 	m_gamedef(gamedef),
86 	m_circuit(circuit),
87 	m_blocks_update_last(0),
88 	m_blocks_save_last(0)
89 {
90 	updateLighting_last[LIGHTBANK_DAY] = updateLighting_last[LIGHTBANK_NIGHT] = 0;
91 }
92 
~Map()93 Map::~Map()
94 {
95 	auto lock = m_blocks.lock_unique_rec();
96 #ifndef SERVER
97 	for(auto &i : m_blocks) {
98 		// We dont have gamedef here anymore, so we cant remove the hardwarebuffers
99 		if(i.second->mesh)
100 			i.second->mesh->clearHardwareBuffer = false;
101 	}
102 #endif
103 }
104 
addEventReceiver(MapEventReceiver * event_receiver)105 void Map::addEventReceiver(MapEventReceiver *event_receiver)
106 {
107 	m_event_receivers.insert(event_receiver);
108 }
109 
removeEventReceiver(MapEventReceiver * event_receiver)110 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
111 {
112 	m_event_receivers.erase(event_receiver);
113 }
114 
dispatchEvent(MapEditEvent * event)115 void Map::dispatchEvent(MapEditEvent *event)
116 {
117 	for(std::set<MapEventReceiver*>::iterator
118 			i = m_event_receivers.begin();
119 			i != m_event_receivers.end(); ++i)
120 	{
121 		(*i)->onMapEditEvent(event);
122 	}
123 }
124 
getBlockNoCreate(v3s16 p3d)125 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
126 {
127 	MapBlock *block = getBlockNoCreateNoEx(p3d);
128 	if(block == NULL)
129 		throw InvalidPositionException("getBlockNoCreate block=NULL");
130 	return block;
131 }
132 
isNodeUnderground(v3s16 p)133 bool Map::isNodeUnderground(v3s16 p)
134 {
135 	v3s16 blockpos = getNodeBlockPos(p);
136 	try{
137 		MapBlock * block = getBlockNoCreate(blockpos);
138 		return block->getIsUnderground();
139 	}
140 	catch(InvalidPositionException &e)
141 	{
142 		return false;
143 	}
144 }
145 
isValidPosition(v3s16 p)146 bool Map::isValidPosition(v3s16 p)
147 {
148 	v3s16 blockpos = getNodeBlockPos(p);
149 	MapBlock *block = getBlockNoCreate(blockpos);
150 	return (block != NULL);
151 }
152 
153 // Returns a CONTENT_IGNORE node if not found
getNodeNoEx(v3s16 p,bool * is_valid_position)154 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
155 {
156 #ifndef NDEBUG
157 	ScopeProfiler sp(g_profiler, "Map: getNodeNoEx");
158 #endif
159 
160 	v3s16 blockpos = getNodeBlockPos(p);
161 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
162 	if (block == NULL) {
163 		if (is_valid_position != NULL)
164 			*is_valid_position = false;
165 		return MapNode(CONTENT_IGNORE);
166 	}
167 
168 	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 	bool is_valid_p;
170 	MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
171 	if (is_valid_position != NULL)
172 		*is_valid_position = is_valid_p;
173 	return node;
174 }
175 
getNodeTry(v3POS p)176 MapNode Map::getNodeTry(v3POS p)
177 {
178 #ifndef NDEBUG
179 	ScopeProfiler sp(g_profiler, "Map: getNodeTry");
180 #endif
181 	auto blockpos = getNodeBlockPos(p);
182 	auto block = getBlockNoCreateNoEx(blockpos, true);
183 	if(!block)
184 		return MapNode(CONTENT_IGNORE);
185 	auto relpos = p - blockpos*MAP_BLOCKSIZE;
186 	return block->getNodeTry(relpos);
187 }
188 
189 /*
190 MapNode Map::getNodeLog(v3POS p){
191 	auto blockpos = getNodeBlockPos(p);
192 	auto block = getBlockNoCreateNoEx(blockpos);
193 	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 	auto node = block->getNodeNoEx(relpos);
195 	infostream<<"getNodeLog("<<p<<") blockpos="<<blockpos<<" block="<<block<<" relpos="<<relpos<<" n="<<node<<std::endl;
196 	return node;
197 }
198 */
199 
200 /*
201 MapNode Map::getNodeNoLock(v3s16 p) //dont use
202 {
203 	v3s16 blockpos = getNodeBlockPos(p);
204 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
205 	if(block == NULL)
206 		return MapNode(CONTENT_IGNORE);
207 	return block->getNodeNoLock(p - blockpos*MAP_BLOCKSIZE);
208 }
209 */
210 
211 #if 0
212 // Deprecated
213 // throws InvalidPositionException if not found
214 // TODO: Now this is deprecated, getNodeNoEx should be renamed
215 MapNode Map::getNode(v3s16 p)
216 {
217 	v3s16 blockpos = getNodeBlockPos(p);
218 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
219 	if (block == NULL)
220 		throw InvalidPositionException("getNode block=NULL");
221 	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
222 	bool is_valid_position;
223 	MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
224 	if (!is_valid_position)
225 		throw InvalidPositionException();
226 	return node;
227 }
228 #endif
229 
230 // throws InvalidPositionException if not found
setNode(v3s16 p,MapNode & n)231 void Map::setNode(v3s16 p, MapNode & n)
232 {
233 	v3s16 blockpos = getNodeBlockPos(p);
234 	MapBlock *block = getBlockNoCreate(blockpos);
235 	v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
236 	// Never allow placing CONTENT_IGNORE, it fucks up stuff
237 	if(n.getContent() == CONTENT_IGNORE){
238 		bool temp_bool;
239 		errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
240 				<<" while trying to replace \""
241 				<<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
242 				<<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
243 		debug_stacks_print_to(infostream);
244 		return;
245 	}
246 	block->setNodeNoCheck(relpos, n);
247 }
248 
249 
250 /*
251 	Goes recursively through the neighbours of the node.
252 
253 	Alters only transparent nodes.
254 
255 	If the lighting of the neighbour is lower than the lighting of
256 	the node was (before changing it to 0 at the step before), the
257 	lighting of the neighbour is set to 0 and then the same stuff
258 	repeats for the neighbour.
259 
260 	The ending nodes of the routine are stored in light_sources.
261 	This is useful when a light is removed. In such case, this
262 	routine can be called for the light node and then again for
263 	light_sources to re-light the area without the removed light.
264 
265 	values of from_nodes are lighting values.
266 */
unspreadLight(enum LightBank bank,std::map<v3s16,u8> & from_nodes,std::set<v3s16> & light_sources,std::map<v3s16,MapBlock * > & modified_blocks)267 void Map::unspreadLight(enum LightBank bank,
268 		std::map<v3s16, u8> & from_nodes,
269 		std::set<v3s16> & light_sources,
270 		std::map<v3s16, MapBlock*>  & modified_blocks)
271 {
272 	INodeDefManager *nodemgr = m_gamedef->ndef();
273 
274 	v3s16 dirs[6] = {
275 		v3s16(0,0,1), // back
276 		v3s16(0,1,0), // top
277 		v3s16(1,0,0), // right
278 		v3s16(0,0,-1), // front
279 		v3s16(0,-1,0), // bottom
280 		v3s16(-1,0,0), // left
281 	};
282 
283 	if(from_nodes.size() == 0)
284 		return;
285 
286 	u32 blockchangecount = 0;
287 
288 	std::map<v3s16, u8> unlighted_nodes;
289 
290 	/*
291 		Initialize block cache
292 	*/
293 	v3s16 blockpos_last;
294 	MapBlock *block = NULL;
295 	// Cache this a bit, too
296 	bool block_checked_in_modified = false;
297 
298 	for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
299 		j != from_nodes.end(); ++j)
300 	{
301 		v3s16 pos = j->first;
302 		v3s16 blockpos = getNodeBlockPos(pos);
303 
304 		// Only fetch a new block if the block position has changed
305 		try{
306 			if(block == NULL || blockpos != blockpos_last){
307 				block = getBlockNoCreate(blockpos);
308 				blockpos_last = blockpos;
309 
310 				block_checked_in_modified = false;
311 				blockchangecount++;
312 			}
313 		}
314 		catch(InvalidPositionException &e)
315 		{
316 			continue;
317 		}
318 
319 		if(!block || block->isDummy())
320 			continue;
321 
322 		// Calculate relative position in block
323 		//v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
324 
325 		// Get node straight from the block
326 		//MapNode n = block->getNode(relpos);
327 
328 		u8 oldlight = j->second;
329 
330 		// Loop through 6 neighbors
331 		for(u16 i=0; i<6; i++)
332 		{
333 			// Get the position of the neighbor node
334 			v3s16 n2pos = pos + dirs[i];
335 
336 			// Get the block where the node is located
337 			v3s16 blockpos = getNodeBlockPos(n2pos);
338 
339 			// Only fetch a new block if the block position has changed
340 			try {
341 				if(block == NULL || blockpos != blockpos_last){
342 					block = getBlockNoCreate(blockpos);
343 
344 					if (!block || block->isDummy())
345 						continue;
346 
347 					blockpos_last = blockpos;
348 
349 					block_checked_in_modified = false;
350 					blockchangecount++;
351 				}
352 			}
353 			catch(InvalidPositionException &e) {
354 				continue;
355 			}
356 
357 			// Calculate relative position in block
358 			v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
359 			// Get node straight from the block
360 			bool is_valid_position;
361 			MapNode n2 = block->getNode(relpos, &is_valid_position);
362 			if (!is_valid_position)
363 				continue;
364 
365 			bool changed = false;
366 
367 			//TODO: Optimize output by optimizing light_sources?
368 
369 			/*
370 				If the neighbor is dimmer than what was specified
371 				as oldlight (the light of the previous node)
372 			*/
373 			if(n2.getLight(bank, nodemgr) < oldlight)
374 			{
375 				/*
376 					And the neighbor is transparent and it has some light
377 				*/
378 				if(nodemgr->get(n2).light_propagates
379 						&& n2.getLight(bank, nodemgr) != 0)
380 				{
381 					/*
382 						Set light to 0 and add to queue
383 					*/
384 
385 					u8 current_light = n2.getLight(bank, nodemgr);
386 					n2.setLight(bank, 0, nodemgr);
387 					block->setNode(relpos, n2);
388 
389 					unlighted_nodes[n2pos] = current_light;
390 					changed = true;
391 
392 					/*
393 						Remove from light_sources if it is there
394 						NOTE: This doesn't happen nearly at all
395 					*/
396 					/*if(light_sources.find(n2pos))
397 					{
398 						infostream<<"Removed from light_sources"<<std::endl;
399 						light_sources.remove(n2pos);
400 					}*/
401 				}
402 
403 				/*// DEBUG
404 				if(light_sources.find(n2pos) != NULL)
405 					light_sources.remove(n2pos);*/
406 			}
407 			else{
408 				light_sources.insert(n2pos);
409 			}
410 
411 			// Add to modified_blocks
412 			if(changed == true && block_checked_in_modified == false)
413 			{
414 				// If the block is not found in modified_blocks, add.
415 				if(modified_blocks.find(blockpos) == modified_blocks.end())
416 				{
417 					modified_blocks[blockpos] = block;
418 				}
419 				block_checked_in_modified = true;
420 			}
421 		}
422 	}
423 
424 	/*infostream<<"unspreadLight(): Changed block "
425 			<<blockchangecount<<" times"
426 			<<" for "<<from_nodes.size()<<" nodes"
427 			<<std::endl;*/
428 
429 	if(unlighted_nodes.size() > 0)
430 		unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
431 }
432 
433 /*
434 	A single-node wrapper of the above
435 */
unLightNeighbors(enum LightBank bank,v3s16 pos,u8 lightwas,std::set<v3s16> & light_sources,std::map<v3s16,MapBlock * > & modified_blocks)436 void Map::unLightNeighbors(enum LightBank bank,
437 		v3s16 pos, u8 lightwas,
438 		std::set<v3s16> & light_sources,
439 		std::map<v3s16, MapBlock*>  & modified_blocks)
440 {
441 	std::map<v3s16, u8> from_nodes;
442 	from_nodes[pos] = lightwas;
443 
444 	unspreadLight(bank, from_nodes, light_sources, modified_blocks);
445 }
446 
447 /*
448 	Lights neighbors of from_nodes, collects all them and then
449 	goes on recursively.
450 */
spreadLight(enum LightBank bank,std::set<v3s16> & from_nodes,std::map<v3s16,MapBlock * > & modified_blocks,int recursive)451 void Map::spreadLight(enum LightBank bank,
452 		std::set<v3s16> & from_nodes,
453 		std::map<v3s16, MapBlock*> & modified_blocks, int recursive)
454 {
455 	INodeDefManager *nodemgr = m_gamedef->ndef();
456 
457 	const v3s16 dirs[6] = {
458 		v3s16(0,0,1), // back
459 		v3s16(0,1,0), // top
460 		v3s16(1,0,0), // right
461 		v3s16(0,0,-1), // front
462 		v3s16(0,-1,0), // bottom
463 		v3s16(-1,0,0), // left
464 	};
465 
466 	if(from_nodes.size() == 0)
467 		return;
468 
469 	u32 blockchangecount = 0;
470 
471 	std::set<v3s16> lighted_nodes;
472 
473 	/*
474 		Initialize block cache
475 	*/
476 	v3s16 blockpos_last;
477 	MapBlock *block = NULL;
478 	// Cache this a bit, too
479 	bool block_checked_in_modified = false;
480 
481 	for(std::set<v3s16>::iterator j = from_nodes.begin();
482 		j != from_nodes.end(); ++j)
483 	{
484 		v3s16 pos = *j;
485 		v3s16 blockpos = getNodeBlockPos(pos);
486 
487 		// Only fetch a new block if the block position has changed
488 		try {
489 			if(block == NULL || blockpos != blockpos_last){
490 				block = getBlockNoCreate(blockpos);
491 				blockpos_last = blockpos;
492 
493 				block_checked_in_modified = false;
494 				blockchangecount++;
495 			}
496 		}
497 		catch(InvalidPositionException &e) {
498 			continue;
499 		}
500 
501 		if(block->isDummy())
502 			continue;
503 
504 		//auto lock = block->try_lock_unique_rec();
505 		//if (!lock->owns_lock())
506 		//	continue;
507 
508 		// Calculate relative position in block
509 		v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
510 
511 		// Get node straight from the block
512 		bool is_valid_position;
513 		MapNode n = block->getNode(relpos, &is_valid_position);
514 		if (n.getContent() == CONTENT_IGNORE)
515 			continue;
516 
517 		u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
518 		u8 newlight = diminish_light(oldlight);
519 
520 		// Loop through 6 neighbors
521 		for(u16 i=0; i<6; i++){
522 			// Get the position of the neighbor node
523 			v3s16 n2pos = pos + dirs[i];
524 
525 			// Get the block where the node is located
526 			v3s16 blockpos = getNodeBlockPos(n2pos);
527 
528 			// Only fetch a new block if the block position has changed
529 			try {
530 				if(block == NULL || blockpos != blockpos_last){
531 					block = getBlockNoCreate(blockpos);
532 					blockpos_last = blockpos;
533 
534 					block_checked_in_modified = false;
535 					blockchangecount++;
536 				}
537 			}
538 			catch(InvalidPositionException &e) {
539 				continue;
540 			}
541 			// Calculate relative position in block
542 			v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
543 			// Get node straight from the block
544 			MapNode n2 = block->getNode(relpos, &is_valid_position);
545 			if (!is_valid_position)
546 				continue;
547 
548 			bool changed = false;
549 			/*
550 				If the neighbor is brighter than the current node,
551 				add to list (it will light up this node on its turn)
552 			*/
553 			if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
554 			{
555 				lighted_nodes.insert(n2pos);
556 				changed = true;
557 			}
558 			/*
559 				If the neighbor is dimmer than how much light this node
560 				would spread on it, add to list
561 			*/
562 			if(n2.getLight(bank, nodemgr) < newlight)
563 			{
564 				if(nodemgr->get(n2).light_propagates)
565 				{
566 					n2.setLight(bank, newlight, nodemgr);
567 					block->setNode(relpos, n2);
568 					lighted_nodes.insert(n2pos);
569 					changed = true;
570 				}
571 			}
572 
573 			// Add to modified_blocks
574 			if(changed == true && block_checked_in_modified == false)
575 			{
576 				// If the block is not found in modified_blocks, add.
577 				if(modified_blocks.find(blockpos) == modified_blocks.end())
578 				{
579 					modified_blocks[blockpos] = block;
580 				}
581 				block_checked_in_modified = true;
582 			}
583 		}
584 	}
585 
586 	/*infostream<<"spreadLight(): Changed block "
587 			<<blockchangecount<<" times"
588 			<<" for "<<from_nodes.size()<<" nodes"
589 			<<std::endl;*/
590 
591 	if(lighted_nodes.size() > 0 && recursive <= 32) { // maybe 32 too small
592 /*
593 		infostream<<"spreadLight(): recursive("<<count<<"): changed=" <<blockchangecount
594 			<<" from="<<from_nodes.size()
595 			<<" lighted="<<lighted_nodes.size()
596 			<<" modifiedB="<<modified_blocks.size()
597 			<<std::endl;
598 */
599 		spreadLight(bank, lighted_nodes, modified_blocks, ++recursive);
600 	}
601 }
602 
603 /*
604 	A single-node source variation of the above.
605 */
lightNeighbors(enum LightBank bank,v3s16 pos,std::map<v3s16,MapBlock * > & modified_blocks)606 void Map::lightNeighbors(enum LightBank bank,
607 		v3s16 pos,
608 		std::map<v3s16, MapBlock*> & modified_blocks)
609 {
610 	std::set<v3s16> from_nodes;
611 	from_nodes.insert(pos);
612 	spreadLight(bank, from_nodes, modified_blocks);
613 }
614 
getBrightestNeighbour(enum LightBank bank,v3s16 p)615 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
616 {
617 	INodeDefManager *nodemgr = m_gamedef->ndef();
618 
619 	v3s16 dirs[6] = {
620 		v3s16(0,0,1), // back
621 		v3s16(0,1,0), // top
622 		v3s16(1,0,0), // right
623 		v3s16(0,0,-1), // front
624 		v3s16(0,-1,0), // bottom
625 		v3s16(-1,0,0), // left
626 	};
627 
628 	u8 brightest_light = 0;
629 	v3s16 brightest_pos(0,0,0);
630 	bool found_something = false;
631 
632 	// Loop through 6 neighbors
633 	for(u16 i=0; i<6; i++){
634 		// Get the position of the neighbor node
635 		v3s16 n2pos = p + dirs[i];
636 		MapNode n2;
637 		bool is_valid_position;
638 		n2 = getNodeNoEx(n2pos, &is_valid_position);
639 		if (!is_valid_position)
640 			continue;
641 
642 		if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
643 			brightest_light = n2.getLight(bank, nodemgr);
644 			brightest_pos = n2pos;
645 			found_something = true;
646 		}
647 	}
648 
649 	if(found_something == false)
650 		throw InvalidPositionException("getBrightestNeighbour nothing found");
651 
652 	return brightest_pos;
653 }
654 
655 /*
656 	Propagates sunlight down from a node.
657 	Starting point gets sunlight.
658 
659 	Returns the lowest y value of where the sunlight went.
660 
661 	Mud is turned into grass in where the sunlight stops.
662 */
propagateSunlight(v3s16 start,std::map<v3s16,MapBlock * > & modified_blocks)663 s16 Map::propagateSunlight(v3s16 start,
664 		std::map<v3s16, MapBlock*> & modified_blocks)
665 {
666 	INodeDefManager *nodemgr = m_gamedef->ndef();
667 
668 	s16 y = start.Y;
669 	for(; ; y--)
670 	{
671 		v3s16 pos(start.X, y, start.Z);
672 
673 		v3s16 blockpos = getNodeBlockPos(pos);
674 		MapBlock *block;
675 		try{
676 			block = getBlockNoCreate(blockpos);
677 		}
678 		catch(InvalidPositionException &e)
679 		{
680 			break;
681 		}
682 
683 		v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
684 		bool is_valid_position;
685 		MapNode n = block->getNode(relpos, &is_valid_position);
686 		if (!is_valid_position)
687 			break;
688 
689 		if(nodemgr->get(n).sunlight_propagates)
690 		{
691 			n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
692 			block->setNode(relpos, n);
693 
694 			modified_blocks[blockpos] = block;
695 		}
696 		else
697 		{
698 			// Sunlight goes no further
699 			break;
700 		}
701 	}
702 	return y + 1;
703 }
704 
updateLighting(enum LightBank bank,shared_map<v3POS,MapBlock * > & a_blocks,std::map<v3POS,MapBlock * > & modified_blocks,int max_cycle_ms)705 u32 Map::updateLighting(enum LightBank bank,
706 		shared_map<v3POS, MapBlock*> & a_blocks,
707 		std::map<v3POS, MapBlock*> & modified_blocks, int max_cycle_ms)
708 {
709 	INodeDefManager *nodemgr = m_gamedef->ndef();
710 
711 	/*m_dout<<DTIME<<"Map::updateLighting(): "
712 			<<a_blocks.size()<<" blocks."<<std::endl;*/
713 
714 	//TimeTaker timer("updateLighting");
715 
716 	// For debugging
717 	//bool debug=true;
718 	//u32 count_was = modified_blocks.size();
719 
720 	std::map<v3s16, MapBlock*> blocks_to_update;
721 
722 	std::set<v3s16> light_sources;
723 
724 	std::map<v3s16, u8> unlight_from;
725 
726 	int num_bottom_invalid = 0;
727 
728 	//JMutexAutoLock lock2(m_update_lighting_mutex);
729 
730 	{
731 	TimeTaker t("updateLighting: first stuff");
732 
733 	u32 n = 0, calls = 0, end_ms = porting::getTimeMs() + max_cycle_ms;
734 	if(!max_cycle_ms)
735 		updateLighting_last[bank] = 0;
736 	for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
737 		i != a_blocks.end(); ++i)
738 	{
739 			if (n++ < updateLighting_last[bank])
740 				continue;
741 			else
742 				updateLighting_last[bank] = 0;
743 			++calls;
744 
745 		MapBlock *block = getBlockNoCreateNoEx(i->first);
746 		//MapBlock *block = i->second;
747 
748 		for(;;)
749 		{
750 			// Don't bother with dummy blocks.
751 			if(!block || block->isDummy())
752 				break;
753 
754 			auto lock = block->try_lock_unique_rec(); // may cause dark areas
755 			if (!lock->owns_lock())
756 				break;
757 			v3s16 pos = block->getPos();
758 			v3s16 posnodes = block->getPosRelative();
759 			modified_blocks[pos] = block;
760 			blocks_to_update[pos] = block;
761 
762 			/*
763 				Clear all light from block
764 			*/
765 			for(s16 z=0; z<MAP_BLOCKSIZE; z++)
766 			for(s16 x=0; x<MAP_BLOCKSIZE; x++)
767 			for(s16 y=0; y<MAP_BLOCKSIZE; y++)
768 			{
769 				v3s16 p(x,y,z);
770 				bool is_valid_position;
771 				MapNode n = block->getNode(p, &is_valid_position);
772 				if (!is_valid_position) {
773 					/* This would happen when dealing with a
774 					   dummy block.
775 					*/
776 					infostream<<"updateLighting(): InvalidPositionException"
777 							<<std::endl;
778 					continue;
779 				}
780 				u8 oldlight = n.getLight(bank, nodemgr);
781 				n.setLight(bank, 0, nodemgr);
782 				block->setNode(p, n);
783 
784 				// If node sources light, add to list
785 				u8 source = nodemgr->get(n).light_source;
786 				if(source != 0)
787 					light_sources.insert(p + posnodes);
788 
789 				// Collect borders for unlighting
790 				if((x==0 || x == MAP_BLOCKSIZE-1
791 						|| y==0 || y == MAP_BLOCKSIZE-1
792 						|| z==0 || z == MAP_BLOCKSIZE-1)
793 						&& oldlight != 0)
794 				{
795 					v3s16 p_map = p + posnodes;
796 					unlight_from[p_map] = oldlight;
797 				}
798 
799 
800 			}
801 
802 			if(bank == LIGHTBANK_DAY)
803 			{
804 				bool bottom_valid = block->propagateSunlight(light_sources);
805 
806 				if(!bottom_valid)
807 					num_bottom_invalid++;
808 
809 				// If bottom is valid, we're done.
810 				if(bottom_valid)
811 					break;
812 			}
813 			else if(bank == LIGHTBANK_NIGHT)
814 			{
815 				// For night lighting, sunlight is not propagated
816 				break;
817 			}
818 
819 			/*infostream<<"Bottom for sunlight-propagated block ("
820 					<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
821 					<<std::endl;*/
822 
823 			block->setLightingExpired(true);
824 
825 			// Bottom sunlight is not valid; get the block and loop to it
826 
827 			pos.Y--;
828 			block = getBlockNoCreateNoEx(pos);
829 		}
830 		if (porting::getTimeMs() > end_ms) {
831 			updateLighting_last[bank] = n;
832 			break;
833 		}
834 	}
835 	if (!calls)
836 		updateLighting_last[bank] = 0;
837 	}
838 	/*
839 		Enable this to disable proper lighting for speeding up map
840 		generation for testing or whatever
841 	*/
842 #if 0
843 	//if(g_settings->get(""))
844 	{
845 		core::map<v3s16, MapBlock*>::Iterator i;
846 		i = blocks_to_update.getIterator();
847 		for(; i.atEnd() == false; i++)
848 		{
849 			MapBlock *block = i.getNode()->getValue();
850 			v3s16 p = block->getPos();
851 			block->setLightingExpired(false);
852 		}
853 		return;
854 	}
855 #endif
856 
857 #if 1
858 	{
859 		TimeTaker timer("updateLighting: unspreadLight");
860 		unspreadLight(bank, unlight_from, light_sources, modified_blocks);
861 	}
862 
863 	/*if(debug)
864 	{
865 		u32 diff = modified_blocks.size() - count_was;
866 		count_was = modified_blocks.size();
867 		infostream<<"unspreadLight modified "<<diff<<std::endl;
868 	}*/
869 
870 	{
871 		TimeTaker timer("updateLighting: spreadLight");
872 		spreadLight(bank, light_sources, modified_blocks);
873 	}
874 
875 	for (auto & ir : blocks_to_update) {
876 		auto block = getBlockNoCreateNoEx(ir.first);
877 		block->setLightingExpired(false);
878 	}
879 
880 	/*if(debug)
881 	{
882 		u32 diff = modified_blocks.size() - count_was;
883 		count_was = modified_blocks.size();
884 		infostream<<"spreadLight modified "<<diff<<std::endl;
885 	}*/
886 #endif
887 
888 #if 0
889 	{
890 		//MapVoxelManipulator vmanip(this);
891 
892 		// Make a manual voxel manipulator and load all the blocks
893 		// that touch the requested blocks
894 		ManualMapVoxelManipulator vmanip(this);
895 
896 		{
897 		//TimeTaker timer("initialEmerge");
898 
899 		core::map<v3s16, MapBlock*>::Iterator i;
900 		i = blocks_to_update.getIterator();
901 		for(; i.atEnd() == false; i++)
902 		{
903 			MapBlock *block = i.getNode()->getValue();
904 			v3s16 p = block->getPos();
905 
906 			// Add all surrounding blocks
907 			vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
908 
909 			/*
910 				Add all surrounding blocks that have up-to-date lighting
911 				NOTE: This doesn't quite do the job (not everything
912 					  appropriate is lighted)
913 			*/
914 			/*for(s16 z=-1; z<=1; z++)
915 			for(s16 y=-1; y<=1; y++)
916 			for(s16 x=-1; x<=1; x++)
917 			{
918 				v3s16 p2 = p + v3s16(x,y,z);
919 				MapBlock *block = getBlockNoCreateNoEx(p2);
920 				if(block == NULL)
921 					continue;
922 				if(block->isDummy())
923 					continue;
924 				if(block->getLightingExpired())
925 					continue;
926 				vmanip.initialEmerge(p2, p2);
927 			}*/
928 
929 			// Lighting of block will be updated completely
930 			block->setLightingExpired(false);
931 		}
932 		}
933 
934 		{
935 			//TimeTaker timer("unSpreadLight");
936 			vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
937 		}
938 		{
939 			//TimeTaker timer("spreadLight");
940 			vmanip.spreadLight(bank, light_sources, nodemgr);
941 		}
942 		{
943 			//TimeTaker timer("blitBack");
944 			vmanip.blitBack(modified_blocks);
945 		}
946 		/*infostream<<"emerge_time="<<emerge_time<<std::endl;
947 		emerge_time = 0;*/
948 	}
949 #endif
950 	return updateLighting_last[bank];
951 	//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
952 }
953 
updateLighting(shared_map<v3POS,MapBlock * > & a_blocks,std::map<v3POS,MapBlock * > & modified_blocks,int max_cycle_ms)954 u32 Map::updateLighting(shared_map<v3POS, MapBlock*> & a_blocks,
955 		std::map<v3POS, MapBlock*> & modified_blocks, int max_cycle_ms)
956 {
957 	int ret = 0;
958 {
959 TimeTaker timer("updateLighting(LIGHTBANK_DAY)");
960 
961 	ret += updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks, max_cycle_ms);
962 }
963 {
964 TimeTaker timer("updateLighting(LIGHTBANK_NIGHT)");
965 	ret += updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks, max_cycle_ms);
966 }
967 
968 	if (max_cycle_ms && ret)
969 		return ret;
970 
971 	a_blocks.clear();
972 TimeTaker timer("updateLighting expireDayNightDiff");
973 	//JMutexAutoLock lock2(m_update_lighting_mutex);
974 
975 	/*
976 		Update information about whether day and night light differ
977 	*/
978 	for(std::map<v3s16, MapBlock*>::iterator
979 			i = modified_blocks.begin();
980 			i != modified_blocks.end(); ++i)
981 	{
982 		MapBlock *block = getBlockNoCreateNoEx(i->first);
983 		//can contain already deleted block from Map::timerUpdate -> MapSector::deleteBlock
984 		//MapBlock *block = i->second;
985 		if(block == NULL || block->isDummy())
986 			continue;
987 		block->expireDayNightDiff();
988 	}
989 	return ret;
990 }
991 
992 /*
993 */
addNodeAndUpdate(v3s16 p,MapNode n,std::map<v3s16,MapBlock * > & modified_blocks,bool remove_metadata)994 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
995 		std::map<v3s16, MapBlock*> &modified_blocks,
996 		bool remove_metadata)
997 {
998 	INodeDefManager *ndef = m_gamedef->ndef();
999 
1000 	/*PrintInfo(m_dout);
1001 	m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
1002 			<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1003 
1004 	/*
1005 		From this node to nodes underneath:
1006 		If lighting is sunlight (1.0), unlight neighbours and
1007 		set lighting to 0.
1008 		Else discontinue.
1009 	*/
1010 
1011 	v3s16 toppos = p + v3s16(0,1,0);
1012 	//v3s16 bottompos = p + v3s16(0,-1,0);
1013 
1014 	bool node_under_sunlight = true;
1015 	std::set<v3s16> light_sources;
1016 
1017 	/*
1018 		Collect old node for rollback
1019 	*/
1020 	RollbackNode rollback_oldnode(this, p, m_gamedef);
1021 
1022 	/*
1023 		If there is a node at top and it doesn't have sunlight,
1024 		there has not been any sunlight going down.
1025 
1026 		Otherwise there probably is.
1027 	*/
1028 
1029 	bool is_valid_position;
1030 	MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1031 
1032 	if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1033 		node_under_sunlight = false;
1034 
1035 	/*
1036 		Remove all light that has come out of this node
1037 	*/
1038 
1039 	enum LightBank banks[] =
1040 	{
1041 		LIGHTBANK_DAY,
1042 		LIGHTBANK_NIGHT
1043 	};
1044 	for(s32 i=0; i<2; i++)
1045 	{
1046 		enum LightBank bank = banks[i];
1047 
1048 		u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
1049 
1050 		// Add the block of the added node to modified_blocks
1051 		v3s16 blockpos = getNodeBlockPos(p);
1052 		MapBlock * block = getBlockNoCreate(blockpos);
1053 		if(!block)
1054 			break;
1055 		modified_blocks[blockpos] = block;
1056 
1057 		// Unlight neighbours of node.
1058 		// This means setting light of all consequent dimmer nodes
1059 		// to 0.
1060 		// This also collects the nodes at the border which will spread
1061 		// light again into this.
1062 		unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1063 
1064 		n.setLight(bank, 0, ndef);
1065 	}
1066 
1067 	/*
1068 		If node lets sunlight through and is under sunlight, it has
1069 		sunlight too.
1070 	*/
1071 	if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1072 	{
1073 		n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1074 	}
1075 
1076 	/*
1077 		Remove node metadata
1078 	*/
1079 	if (remove_metadata) {
1080 		removeNodeMetadata(p);
1081 	}
1082 
1083 	/*
1084 		Set the node on the map
1085 	*/
1086 
1087 	setNode(p, n);
1088 
1089 	/*
1090 		If node is under sunlight and doesn't let sunlight through,
1091 		take all sunlighted nodes under it and clear light from them
1092 		and from where the light has been spread.
1093 		TODO: This could be optimized by mass-unlighting instead
1094 			  of looping
1095 	*/
1096 	if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1097 	{
1098 		s16 y = p.Y - 1;
1099 		for(;; y--){
1100 			//m_dout<<DTIME<<"y="<<y<<std::endl;
1101 			v3s16 n2pos(p.X, y, p.Z);
1102 
1103 			MapNode n2;
1104 
1105 			n2 = getNodeNoEx(n2pos, &is_valid_position);
1106 			if (!is_valid_position)
1107 				break;
1108 
1109 			if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1110 			{
1111 				unLightNeighbors(LIGHTBANK_DAY,
1112 						n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1113 						light_sources, modified_blocks);
1114 				n2.setLight(LIGHTBANK_DAY, 0, ndef);
1115 				setNode(n2pos, n2);
1116 			}
1117 			else
1118 				break;
1119 		}
1120 	}
1121 
1122 	for(s32 i=0; i<2; i++)
1123 	{
1124 		enum LightBank bank = banks[i];
1125 
1126 		/*
1127 			Spread light from all nodes that might be capable of doing so
1128 		*/
1129 		spreadLight(bank, light_sources, modified_blocks);
1130 	}
1131 
1132 	/*
1133 		Update information about whether day and night light differ
1134 	*/
1135 	for(std::map<v3s16, MapBlock*>::iterator
1136 			i = modified_blocks.begin();
1137 			i != modified_blocks.end(); ++i)
1138 	{
1139 		i->second->expireDayNightDiff();
1140 	}
1141 
1142 	/*
1143 		Report for rollback
1144 	*/
1145 	if(m_gamedef->rollback())
1146 	{
1147 		RollbackNode rollback_newnode(this, p, m_gamedef);
1148 		RollbackAction action;
1149 		action.setSetNode(p, rollback_oldnode, rollback_newnode);
1150 		m_gamedef->rollback()->reportAction(action);
1151 	}
1152 
1153 	/*
1154 		Add neighboring liquid nodes and the node itself if it is
1155 		liquid (=water node was added) to transform queue.
1156 		note: todo: for liquid_real enough to add only self node
1157 	*/
1158 	v3s16 dirs[7] = {
1159 		v3s16(0,0,0), // self
1160 		v3s16(0,0,1), // back
1161 		v3s16(0,1,0), // top
1162 		v3s16(1,0,0), // right
1163 		v3s16(0,0,-1), // front
1164 		v3s16(0,-1,0), // bottom
1165 		v3s16(-1,0,0), // left
1166 	};
1167 	for(u16 i=0; i<7; i++)
1168 	{
1169 		v3s16 p2 = p + dirs[i];
1170 
1171 		MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1172 		if(is_valid_position
1173 				&& (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1174 		{
1175 			transforming_liquid_push_back(p2);
1176 		}
1177 	}
1178 }
1179 
1180 /*
1181 */
removeNodeAndUpdate(v3s16 p,std::map<v3s16,MapBlock * > & modified_blocks)1182 void Map::removeNodeAndUpdate(v3s16 p,
1183 		std::map<v3s16, MapBlock*> &modified_blocks)
1184 {
1185 	INodeDefManager *ndef = m_gamedef->ndef();
1186 
1187 	/*PrintInfo(m_dout);
1188 	m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1189 			<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1190 
1191 	bool node_under_sunlight = true;
1192 
1193 	v3s16 toppos = p + v3s16(0,1,0);
1194 
1195 	// Node will be replaced with this
1196 	content_t replace_material = CONTENT_AIR;
1197 
1198 	/*
1199 		Collect old node for rollback
1200 	*/
1201 	RollbackNode rollback_oldnode(this, p, m_gamedef);
1202 
1203 	/*
1204 		If there is a node at top and it doesn't have sunlight,
1205 		there will be no sunlight going down.
1206 	*/
1207 	bool is_valid_position;
1208 	MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1209 
1210 	if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1211 		node_under_sunlight = false;
1212 
1213 	std::set<v3s16> light_sources;
1214 
1215 	enum LightBank banks[] =
1216 	{
1217 		LIGHTBANK_DAY,
1218 		LIGHTBANK_NIGHT
1219 	};
1220 	for(s32 i=0; i<2; i++)
1221 	{
1222 		enum LightBank bank = banks[i];
1223 
1224 		/*
1225 			Unlight neighbors (in case the node is a light source)
1226 		*/
1227 		unLightNeighbors(bank, p,
1228 				getNodeNoEx(p).getLight(bank, ndef),
1229 				light_sources, modified_blocks);
1230 	}
1231 
1232 	/*
1233 		Remove node metadata
1234 	*/
1235 
1236 	removeNodeMetadata(p);
1237 
1238 	/*
1239 		Remove the node.
1240 		This also clears the lighting.
1241 	*/
1242 
1243 	MapNode n;
1244 	n.setContent(replace_material);
1245 	setNode(p, n);
1246 
1247 	for(s32 i=0; i<2; i++)
1248 	{
1249 		enum LightBank bank = banks[i];
1250 
1251 		/*
1252 			Recalculate lighting
1253 		*/
1254 		spreadLight(bank, light_sources, modified_blocks);
1255 	}
1256 
1257 	// Add the block of the removed node to modified_blocks
1258 	v3s16 blockpos = getNodeBlockPos(p);
1259 	MapBlock * block = getBlockNoCreate(blockpos);
1260 	if(!block)
1261 		return;
1262 	modified_blocks[blockpos] = block;
1263 
1264 	/*
1265 		If the removed node was under sunlight, propagate the
1266 		sunlight down from it and then light all neighbors
1267 		of the propagated blocks.
1268 	*/
1269 	if(node_under_sunlight)
1270 	{
1271 		s16 ybottom = propagateSunlight(p, modified_blocks);
1272 		/*m_dout<<DTIME<<"Node was under sunlight. "
1273 				"Propagating sunlight";
1274 		m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1275 		s16 y = p.Y;
1276 		for(; y >= ybottom; y--)
1277 		{
1278 			v3s16 p2(p.X, y, p.Z);
1279 			/*m_dout<<DTIME<<"lighting neighbors of node ("
1280 					<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1281 					<<std::endl;*/
1282 			lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1283 		}
1284 	}
1285 	else
1286 	{
1287 		// Set the lighting of this node to 0
1288 		// TODO: Is this needed? Lighting is cleared up there already.
1289 		MapNode n = getNodeNoEx(p, &is_valid_position);
1290 		if (is_valid_position) {
1291 			n.setLight(LIGHTBANK_DAY, 0, ndef);
1292 			setNode(p, n);
1293 		} else {
1294 			//assert(0);
1295 		}
1296 	}
1297 
1298 	for(s32 i=0; i<2; i++)
1299 	{
1300 		enum LightBank bank = banks[i];
1301 
1302 		// Get the brightest neighbour node and propagate light from it
1303 		v3s16 n2p = getBrightestNeighbour(bank, p);
1304 		try{
1305 			//MapNode n2 = getNode(n2p);
1306 			lightNeighbors(bank, n2p, modified_blocks);
1307 		}
1308 		catch(InvalidPositionException &e)
1309 		{
1310 		}
1311 	}
1312 
1313 	/*
1314 		Update information about whether day and night light differ
1315 	*/
1316 	for(std::map<v3s16, MapBlock*>::iterator
1317 			i = modified_blocks.begin();
1318 			i != modified_blocks.end(); ++i)
1319 	{
1320 		i->second->expireDayNightDiff();
1321 	}
1322 
1323 	/*
1324 		Report for rollback
1325 	*/
1326 	if(m_gamedef->rollback())
1327 	{
1328 		RollbackNode rollback_newnode(this, p, m_gamedef);
1329 		RollbackAction action;
1330 		action.setSetNode(p, rollback_oldnode, rollback_newnode);
1331 		m_gamedef->rollback()->reportAction(action);
1332 	}
1333 
1334 	/*
1335 		Add neighboring liquid nodes and this node to transform queue.
1336 		(it's vital for the node itself to get updated last.)
1337 		note: todo: for liquid_real enough to add only self node
1338 	*/
1339 	v3s16 dirs[7] = {
1340 		v3s16(0,0,1), // back
1341 		v3s16(0,1,0), // top
1342 		v3s16(1,0,0), // right
1343 		v3s16(0,0,-1), // front
1344 		v3s16(0,-1,0), // bottom
1345 		v3s16(-1,0,0), // left
1346 		v3s16(0,0,0), // self
1347 	};
1348 	for(u16 i=0; i<7; i++)
1349 	{
1350 		v3s16 p2 = p + dirs[i];
1351 
1352 		bool is_position_valid;
1353 		MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1354 		if (is_position_valid
1355 				&& (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1356 		{
1357 			transforming_liquid_push_back(p2);
1358 		}
1359 	}
1360 }
1361 
addNodeWithEvent(v3s16 p,MapNode n,bool remove_metadata)1362 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1363 {
1364 	MapEditEvent event;
1365 	event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1366 	event.p = p;
1367 	event.n = n;
1368 
1369 	bool succeeded = true;
1370 	try{
1371 		std::map<v3s16, MapBlock*> modified_blocks;
1372 		addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1373 
1374 		// Copy modified_blocks to event
1375 		for(std::map<v3s16, MapBlock*>::iterator
1376 				i = modified_blocks.begin();
1377 				i != modified_blocks.end(); ++i)
1378 		{
1379 			event.modified_blocks.insert(i->first);
1380 		}
1381 	}
1382 	catch(InvalidPositionException &e){
1383 		succeeded = false;
1384 	}
1385 
1386 	dispatchEvent(&event);
1387 
1388 	return succeeded;
1389 }
1390 
removeNodeWithEvent(v3s16 p)1391 bool Map::removeNodeWithEvent(v3s16 p)
1392 {
1393 	MapEditEvent event;
1394 	event.type = MEET_REMOVENODE;
1395 	event.p = p;
1396 
1397 	bool succeeded = true;
1398 	try{
1399 		std::map<v3s16, MapBlock*> modified_blocks;
1400 		removeNodeAndUpdate(p, modified_blocks);
1401 
1402 		// Copy modified_blocks to event
1403 		for(std::map<v3s16, MapBlock*>::iterator
1404 				i = modified_blocks.begin();
1405 				i != modified_blocks.end(); ++i)
1406 		{
1407 			event.modified_blocks.insert(i->first);
1408 		}
1409 	}
1410 	catch(InvalidPositionException &e){
1411 		succeeded = false;
1412 	}
1413 
1414 	dispatchEvent(&event);
1415 
1416 	return succeeded;
1417 }
1418 
getDayNightDiff(v3s16 blockpos)1419 bool Map::getDayNightDiff(v3s16 blockpos)
1420 {
1421 	try{
1422 		v3s16 p = blockpos + v3s16(0,0,0);
1423 		MapBlock *b = getBlockNoCreate(p);
1424 		if(b->getDayNightDiff())
1425 			return true;
1426 	}
1427 	catch(InvalidPositionException &e){}
1428 	// Leading edges
1429 	try{
1430 		v3s16 p = blockpos + v3s16(-1,0,0);
1431 		MapBlock *b = getBlockNoCreate(p);
1432 		if(b->getDayNightDiff())
1433 			return true;
1434 	}
1435 	catch(InvalidPositionException &e){}
1436 	try{
1437 		v3s16 p = blockpos + v3s16(0,-1,0);
1438 		MapBlock *b = getBlockNoCreate(p);
1439 		if(b->getDayNightDiff())
1440 			return true;
1441 	}
1442 	catch(InvalidPositionException &e){}
1443 	try{
1444 		v3s16 p = blockpos + v3s16(0,0,-1);
1445 		MapBlock *b = getBlockNoCreate(p);
1446 		if(b->getDayNightDiff())
1447 			return true;
1448 	}
1449 	catch(InvalidPositionException &e){}
1450 	// Trailing edges
1451 	try{
1452 		v3s16 p = blockpos + v3s16(1,0,0);
1453 		MapBlock *b = getBlockNoCreate(p);
1454 		if(b->getDayNightDiff())
1455 			return true;
1456 	}
1457 	catch(InvalidPositionException &e){}
1458 	try{
1459 		v3s16 p = blockpos + v3s16(0,1,0);
1460 		MapBlock *b = getBlockNoCreate(p);
1461 		if(b->getDayNightDiff())
1462 			return true;
1463 	}
1464 	catch(InvalidPositionException &e){}
1465 	try{
1466 		v3s16 p = blockpos + v3s16(0,0,1);
1467 		MapBlock *b = getBlockNoCreate(p);
1468 		if(b->getDayNightDiff())
1469 			return true;
1470 	}
1471 	catch(InvalidPositionException &e){}
1472 
1473 	return false;
1474 }
1475 
1476 /*
1477 	Updates usage timers
1478 */
timerUpdate(float uptime,float unload_timeout,int max_cycle_ms,std::list<v3s16> * unloaded_blocks)1479 u32 Map::timerUpdate(float uptime, float unload_timeout,
1480 		int max_cycle_ms,
1481 		std::list<v3s16> *unloaded_blocks)
1482 {
1483 	bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1484 
1485 	// Profile modified reasons
1486 	Profiler modprofiler;
1487 
1488 	if (/*!m_blocks_update_last && */ m_blocks_delete->size() > 1000) {
1489 		m_blocks_delete = (m_blocks_delete == &m_blocks_delete_1 ? &m_blocks_delete_2 : &m_blocks_delete_1);
1490 		verbosestream<<"Deleting blocks="<<m_blocks_delete->size()<<std::endl;
1491 		m_blocks_delete->clear();
1492 	}
1493 
1494 	u32 deleted_blocks_count = 0;
1495 	u32 saved_blocks_count = 0;
1496 	u32 block_count_all = 0;
1497 
1498 	u32 n = 0, calls = 0, end_ms = porting::getTimeMs() + max_cycle_ms;
1499 
1500 	std::vector<MapBlockP> blocks_delete;
1501 	int save_started = 0;
1502 	{
1503 	auto lock = m_blocks.try_lock_shared_rec();
1504 	if (!lock->owns_lock())
1505 		return m_blocks_update_last;
1506 	for(auto ir : m_blocks) {
1507 		if (n++ < m_blocks_update_last) {
1508 			continue;
1509 		}
1510 		else {
1511 			m_blocks_update_last = 0;
1512 		}
1513 		++calls;
1514 
1515 		auto block = ir.second;
1516 		if (!block)
1517 			continue;
1518 
1519 		{
1520 			auto lock = block->try_lock_unique_rec();
1521 			if (!lock->owns_lock())
1522 				continue;
1523 			if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1524 			{
1525 				v3s16 p = block->getPos();
1526 				//infostream<<" deleting block p="<<p<<" ustimer="<<block->getUsageTimer() <<" to="<< unload_timeout<<" inc="<<(uptime - block->m_uptime_timer_last)<<" state="<<block->getModified()<<std::endl;
1527 				// Save if modified
1528 				if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1529 				{
1530 					//modprofiler.add(block->getModifiedReason(), 1);
1531 					if(!save_started++)
1532 						beginSave();
1533 					if (!saveBlock(block.get()))
1534 						continue;
1535 					saved_blocks_count++;
1536 				}
1537 
1538 				blocks_delete.push_back(block);
1539 
1540 				if(unloaded_blocks)
1541 					unloaded_blocks->push_back(p);
1542 
1543 				deleted_blocks_count++;
1544 			}
1545 			else
1546 			{
1547 
1548 			if (!block->m_uptime_timer_last)  // not very good place, but minimum modifications
1549 				block->m_uptime_timer_last = uptime - 0.1;
1550 			block->incrementUsageTimer(uptime - block->m_uptime_timer_last);
1551 			block->m_uptime_timer_last = uptime;
1552 
1553 				block_count_all++;
1554 
1555 /*#ifndef SERVER
1556 				if(block->refGet() == 0 && block->getUsageTimer() >
1557 						g_settings->getFloat("unload_unused_meshes_timeout"))
1558 				{
1559 					if(block->mesh){
1560 						delete block->mesh;
1561 						block->mesh = NULL;
1562 					}
1563 				}
1564 #endif*/
1565 			}
1566 
1567 		} // block lock
1568 
1569 		if (porting::getTimeMs() > end_ms) {
1570 			m_blocks_update_last = n;
1571 			break;
1572 		}
1573 
1574 	}
1575 	}
1576 	if(save_started)
1577 		endSave();
1578 
1579 	if (!calls)
1580 		m_blocks_update_last = 0;
1581 
1582 	for (auto & block : blocks_delete)
1583 		this->deleteBlock(block);
1584 
1585 	if(m_circuit != NULL) {
1586 		m_circuit->save();
1587 	}
1588 
1589 	// Finally delete the empty sectors
1590 
1591 	if(deleted_blocks_count != 0)
1592 	{
1593 		if (m_blocks_update_last)
1594 			infostream<<"ServerMap: timerUpdate(): Blocks processed:"<<calls<<"/"<<m_blocks.size()<<" to "<<m_blocks_update_last<<std::endl;
1595 		PrintInfo(infostream); // ServerMap/ClientMap:
1596 		infostream<<"Unloaded "<<deleted_blocks_count<<"/"<<(block_count_all + deleted_blocks_count)
1597 				<<" blocks from memory";
1598 		infostream<<" (deleteq1="<<m_blocks_delete_1.size()<< " deleteq2="<<m_blocks_delete_2.size()<<")";
1599 		if(saved_blocks_count)
1600 			infostream<<", of which "<<saved_blocks_count<<" were written";
1601 /*
1602 		infostream<<", "<<block_count_all<<" blocks in memory";
1603 */
1604 		infostream<<"."<<std::endl;
1605 		if(saved_blocks_count != 0){
1606 			PrintInfo(infostream); // ServerMap/ClientMap:
1607 			//infostream<<"Blocks modified by: "<<std::endl;
1608 			modprofiler.print(infostream);
1609 		}
1610 	}
1611 	return m_blocks_update_last;
1612 }
1613 
unloadUnreferencedBlocks(std::list<v3s16> * unloaded_blocks)1614 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1615 {
1616 	timerUpdate(0.0, -1.0, 100, unloaded_blocks);
1617 }
1618 
PrintInfo(std::ostream & out)1619 void Map::PrintInfo(std::ostream &out)
1620 {
1621 	out<<"Map: ";
1622 }
1623 
1624 enum NeighborType {
1625 	NEIGHBOR_UPPER,
1626 	NEIGHBOR_SAME_LEVEL,
1627 	NEIGHBOR_LOWER
1628 };
1629 struct NodeNeighbor {
1630 	MapNode n;
1631 	NeighborType t;
1632 	v3s16 p;
1633 	content_t c;
1634 	bool l; //can liquid
1635 	bool i; //infinity
1636 	int weight;
1637 	int drop; //drop by liquid
1638 };
1639 
transforming_liquid_push_back(v3s16 p)1640 void Map::transforming_liquid_push_back(v3s16 p) {
1641 	//JMutexAutoLock lock(m_transforming_liquid_mutex);
1642 	m_transforming_liquid.set(p, 1);
1643 }
1644 
transforming_liquid_pop()1645 v3s16 Map::transforming_liquid_pop() {
1646 	//auto lock = m_transforming_liquid.lock_unique_rec();
1647 	auto it = m_transforming_liquid.begin();
1648 	auto value = it->first;
1649 	m_transforming_liquid.erase(it);
1650 	return value;
1651 }
1652 
transforming_liquid_size()1653 u32 Map::transforming_liquid_size() {
1654 	return m_transforming_liquid.size();
1655 }
1656 
getCircuit()1657 Circuit* Map::getCircuit()
1658 {
1659 	return m_circuit;
1660 }
1661 
getNodeDefManager()1662 INodeDefManager* Map::getNodeDefManager()
1663 {
1664 	return m_gamedef->ndef();
1665 }
1666 
1667 const v3POS liquid_flow_dirs[7] =
1668 {
1669 	// +right, +top, +back
1670 	v3POS( 0,-1, 0), // bottom
1671 	v3POS( 0, 0, 0), // self
1672 	v3POS( 0, 0, 1), // back
1673 	v3POS( 0, 0,-1), // front
1674 	v3POS( 1, 0, 0), // right
1675 	v3POS(-1, 0, 0), // left
1676 	v3POS( 0, 1, 0)  // top
1677 };
1678 
1679 // when looking around we must first check self node for correct type definitions
1680 const s8 liquid_explore_map[7] = {1,0,6,2,3,4,5};
1681 const s8 liquid_random_map[4][7] = {
1682 	{0,1,2,3,4,5,6},
1683 	{0,1,4,3,5,2,6},
1684 	{0,1,3,5,4,2,6},
1685 	{0,1,5,3,2,4,6}
1686 };
1687 
1688 #define D_BOTTOM 0
1689 #define D_TOP 6
1690 #define D_SELF 1
1691 
transformLiquidsReal(Server * m_server,int max_cycle_ms)1692 u32 Map::transformLiquidsReal(Server *m_server, int max_cycle_ms)
1693 {
1694 	INodeDefManager *nodemgr = m_gamedef->ndef();
1695 
1696 	DSTACK(__FUNCTION_NAME);
1697 	//TimeTaker timer("transformLiquidsReal()");
1698 	u32 loopcount = 0;
1699 	u32 initial_size = transforming_liquid_size();
1700 
1701 	u8 relax = g_settings->getS16("liquid_relax");
1702 	bool fast_flood = g_settings->getS16("liquid_fast_flood");
1703 	int water_level = g_settings->getS16("water_level");
1704 
1705 	// list of nodes that due to viscosity have not reached their max level height
1706 	std::unordered_map<v3POS, bool, v3POSHash, v3POSEqual> must_reflow, must_reflow_second, must_reflow_third;
1707 	// List of MapBlocks that will require a lighting update (due to lava)
1708 	u16 loop_rand = myrand();
1709 
1710 	u32 end_ms = porting::getTimeMs() + max_cycle_ms;
1711 
1712 	NEXT_LIQUID:;
1713 	while (transforming_liquid_size() > 0)
1714 	{
1715 		// This should be done here so that it is done when continue is used
1716 		if (loopcount >= initial_size*2 || porting::getTimeMs() > end_ms)
1717 			break;
1718 		++loopcount;
1719 		/*
1720 			Get a queued transforming liquid node
1721 		*/
1722 		v3POS p0;
1723 		{
1724 			//JMutexAutoLock lock(m_transforming_liquid_mutex);
1725 			p0 = transforming_liquid_pop();
1726 		}
1727 		u16 total_level = 0;
1728 		//u16 level_max = 0;
1729 		// surrounding flowing liquid nodes
1730 		NodeNeighbor neighbors[7];
1731 		// current level of every block
1732 		s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1};
1733 		 // target levels
1734 		s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1};
1735 		s8 can_liquid_same_level = 0;
1736 		content_t liquid_kind = CONTENT_IGNORE;
1737 		content_t liquid_kind_flowing = CONTENT_IGNORE;
1738 		content_t melt_kind = CONTENT_IGNORE;
1739 		content_t melt_kind_flowing = CONTENT_IGNORE;
1740 		//s8 viscosity = 0;
1741 		/*
1742 			Collect information about the environment, start from self
1743 		 */
1744 		for (u8 e = 0; e < 7; e++) {
1745 			u8 i = liquid_explore_map[e];
1746 			NodeNeighbor & nb = neighbors[i];
1747 			nb.p = p0 + liquid_flow_dirs[i];
1748 			nb.n = getNodeNoEx(neighbors[i].p);
1749 			nb.c = nb.n.getContent();
1750 			NeighborType nt = NEIGHBOR_SAME_LEVEL;
1751 			switch (i) {
1752 				case D_TOP:
1753 					nt = NEIGHBOR_UPPER;
1754 					break;
1755 				case D_BOTTOM:
1756 					nt = NEIGHBOR_LOWER;
1757 					break;
1758 			}
1759 			nb.t = nt;
1760 			nb.l = 0;
1761 			nb.i = 0;
1762 			nb.weight = 0;
1763 			nb.drop = 0;
1764 
1765 			if (nb.c == CONTENT_IGNORE) {
1766 				//if (i == D_SELF && (loopcount % 2) && initial_size < m_liquid_step_flow * 3)
1767 				//	must_reflow_third[nb.p] = 1;
1768 				continue;
1769 			}
1770 
1771 			switch (nodemgr->get(nb.c).liquid_type) {
1772 				case LIQUID_NONE:
1773 					if (nb.c == CONTENT_AIR) {
1774 						liquid_levels[i] = 0;
1775 						nb.l = 1;
1776 					}
1777 					//TODO: if (nb.c == CONTENT_AIR || nodemgr->get(nb.n).buildable_to && !nodemgr->get(nb.n).walkable) { // need lua drop api for drop torches
1778 					else if (	melt_kind_flowing != CONTENT_IGNORE &&
1779 							nb.c == melt_kind_flowing &&
1780 							nb.t != NEIGHBOR_UPPER &&
1781 							!(loopcount % 2)) {
1782 						u8 melt_max_level = nb.n.getMaxLevel(nodemgr);
1783 						u8 my_max_level = MapNode(liquid_kind_flowing).getMaxLevel(nodemgr);
1784 						liquid_levels[i] = (float)my_max_level / melt_max_level * nb.n.getLevel(nodemgr);
1785 						if (liquid_levels[i])
1786 							nb.l = 1;
1787 					} else if (	melt_kind != CONTENT_IGNORE &&
1788 							nb.c == melt_kind &&
1789 							nb.t != NEIGHBOR_UPPER &&
1790 							!(loopcount % 8)) {
1791 						liquid_levels[i] = nodemgr->get(liquid_kind_flowing).getMaxLevel();
1792 						if (liquid_levels[i])
1793 							nb.l = 1;
1794 					} else {
1795 						int drop = ((ItemGroupList) nodemgr->get(nb.n).groups)["drop_by_liquid"];
1796 						if (drop && !(loopcount % drop) ) {
1797 							liquid_levels[i] = 0;
1798 							nb.l = 1;
1799 							nb.drop = 1;
1800 						}
1801 					}
1802 
1803 					// todo: for erosion add something here..
1804 					break;
1805 				case LIQUID_SOURCE:
1806 					// if this node is not (yet) of a liquid type,
1807 					// choose the first liquid type we encounter
1808 					if (liquid_kind_flowing == CONTENT_IGNORE)
1809 						liquid_kind_flowing = nodemgr->getId(
1810 							nodemgr->get(nb.n).liquid_alternative_flowing);
1811 					if (liquid_kind == CONTENT_IGNORE)
1812 						liquid_kind = nb.c;
1813 					if (liquid_kind_flowing == CONTENT_IGNORE)
1814 						liquid_kind_flowing = liquid_kind;
1815 					if (melt_kind == CONTENT_IGNORE)
1816 						melt_kind = nodemgr->getId(nodemgr->get(nb.n).melt);
1817 					if (melt_kind_flowing == CONTENT_IGNORE)
1818 						melt_kind_flowing =
1819 							nodemgr->getId(
1820 							nodemgr->get(nodemgr->getId(nodemgr->get(nb.n).melt)
1821 									).liquid_alternative_flowing);
1822 					if (melt_kind_flowing == CONTENT_IGNORE)
1823 						melt_kind_flowing = melt_kind;
1824 					if (nb.c == liquid_kind) {
1825 						liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE;
1826 						nb.l = 1;
1827 						nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1828 					}
1829 					break;
1830 				case LIQUID_FLOWING:
1831 					// if this node is not (yet) of a liquid type,
1832 					// choose the first liquid type we encounter
1833 					if (liquid_kind_flowing == CONTENT_IGNORE)
1834 						liquid_kind_flowing = nb.c;
1835 					if (liquid_kind == CONTENT_IGNORE)
1836 						liquid_kind = nodemgr->getId(
1837 							nodemgr->get(nb.n).liquid_alternative_source);
1838 					if (liquid_kind == CONTENT_IGNORE)
1839 						liquid_kind = liquid_kind_flowing;
1840 					if (melt_kind_flowing == CONTENT_IGNORE)
1841 						melt_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).melt);
1842 					if (melt_kind == CONTENT_IGNORE)
1843 						melt_kind = nodemgr->getId(nodemgr->get(nodemgr->getId(
1844 							nodemgr->get(nb.n).melt)).liquid_alternative_source);
1845 					if (melt_kind == CONTENT_IGNORE)
1846 						melt_kind = melt_kind_flowing;
1847 					if (nb.c == liquid_kind_flowing) {
1848 						liquid_levels[i] = nb.n.getLevel(nodemgr);
1849 						nb.l = 1;
1850 					}
1851 					break;
1852 			}
1853 
1854 			// only self, top, bottom swap
1855 			if (nodemgr->get(nb.c).liquid_type && e <= 2) {
1856 				try{
1857 					nb.weight = ((ItemGroupList) nodemgr->get(nb.n).groups)["weight"];
1858 					if (e == 1 && neighbors[D_BOTTOM].weight && neighbors[D_SELF].weight > neighbors[D_BOTTOM].weight) {
1859 						setNode(neighbors[D_SELF].p, neighbors[D_BOTTOM].n);
1860 						setNode(neighbors[D_BOTTOM].p, neighbors[D_SELF].n);
1861 						must_reflow_second[neighbors[D_SELF].p] = 1;
1862 						must_reflow_second[neighbors[D_BOTTOM].p] = 1;
1863 						goto NEXT_LIQUID;
1864 					}
1865 					if (e == 2 && neighbors[D_SELF].weight && neighbors[D_TOP].weight > neighbors[D_SELF].weight) {
1866 						setNode(neighbors[D_SELF].p, neighbors[D_TOP].n);
1867 						setNode(neighbors[D_TOP].p, neighbors[D_SELF].n);
1868 						must_reflow_second[neighbors[D_SELF].p] = 1;
1869 						must_reflow_second[neighbors[D_TOP].p] = 1;
1870 						goto NEXT_LIQUID;
1871 					}
1872 				}
1873 				catch(InvalidPositionException &e) {
1874 					verbosestream<<"transformLiquidsReal: weight: setNode() failed:"<< nb.p<<":"<<e.what()<<std::endl;
1875 					//goto NEXT_LIQUID;
1876 				}
1877 			}
1878 
1879 			if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL)
1880 				++can_liquid_same_level;
1881 			if (liquid_levels[i] > 0)
1882 				total_level += liquid_levels[i];
1883 
1884 			/*
1885 			infostream << "get node i=" <<(int)i<<" " << PP(nb.p) << " c="
1886 			<< nb.c <<" p0="<< (int)nb.n.param0 <<" p1="
1887 			<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="
1888 			<< nodemgr->get(nb.c).liquid_type
1889 			//<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1890 			<< " l="<< nb.l	<< " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i]
1891 			<< " totallevel=" << (int)total_level << " cansame="
1892 			<< (int)can_liquid_same_level << " Lmax="<<(int)nodemgr->get(liquid_kind_flowing).getMaxLevel()<<std::endl;
1893 			*/
1894 		}
1895 		s16 level_max = nodemgr->get(liquid_kind_flowing).getMaxLevel();
1896 		s16 level_max_compressed = nodemgr->get(liquid_kind_flowing).getMaxLevel(1);
1897 		//s16 total_was = total_level; //debug
1898 		//viscosity = nodemgr->get(liquid_kind).viscosity;
1899 
1900 		if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0)
1901 			continue;
1902 
1903 		// fill bottom block
1904 		if (neighbors[D_BOTTOM].l) {
1905 			liquid_levels_want[D_BOTTOM] = total_level > level_max ?
1906 				level_max : total_level;
1907 			total_level -= liquid_levels_want[D_BOTTOM];
1908 		}
1909 
1910 		//relax up
1911 		if (	nodemgr->get(liquid_kind).liquid_renewable &&
1912 			relax &&
1913 			((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) &&
1914 			level_max > 1 &&
1915 			liquid_levels[D_TOP] == 0 &&
1916 			liquid_levels[D_BOTTOM] == level_max &&
1917 			total_level >= level_max * can_liquid_same_level - (can_liquid_same_level - relax) &&
1918 			can_liquid_same_level >= relax + 1) {
1919 			total_level = level_max * can_liquid_same_level;
1920 		}
1921 
1922 		// prevent lakes in air above unloaded blocks
1923 		if (	liquid_levels[D_TOP] == 0 &&
1924 			p0.Y > water_level &&
1925 			level_max > 1 &&
1926 			neighbors[D_BOTTOM].c == CONTENT_IGNORE &&
1927 			!(loopcount % 3)) {
1928 			--total_level;
1929 		}
1930 
1931 		// calculate self level 5 blocks
1932 		u16 want_level =
1933 			  total_level >= level_max * can_liquid_same_level
1934 			? level_max
1935 			: total_level / can_liquid_same_level;
1936 		total_level -= want_level * can_liquid_same_level;
1937 
1938 		//relax down
1939 		if (	nodemgr->get(liquid_kind).liquid_renewable &&
1940 			relax &&
1941 			p0.Y == water_level &&
1942 			liquid_levels[D_TOP] == 0 &&
1943 			!(loopcount % 2) &&
1944 			level_max > 1 &&
1945 			liquid_levels[D_BOTTOM] == level_max &&
1946 			want_level == 0 &&
1947 			total_level <= (can_liquid_same_level - relax) &&
1948 			can_liquid_same_level >= relax + 1) {
1949 			total_level = 0;
1950 		}
1951 
1952 		for (u16 ir = D_SELF; ir < D_TOP; ++ir) { // fill only same level
1953 			u16 ii = liquid_random_map[(loopcount+loop_rand+1)%4][ir];
1954 			if (!neighbors[ii].l)
1955 				continue;
1956 			liquid_levels_want[ii] = want_level;
1957 			//if (viscosity > 1 && (liquid_levels_want[ii]-liquid_levels[ii]>8-viscosity))
1958 			if (liquid_levels_want[ii] < level_max && total_level > 0) {
1959 				if (level_max > LIQUID_LEVEL_SOURCE || loopcount % 3 || liquid_levels[ii] <= 0){
1960 					if (liquid_levels[ii] > liquid_levels_want[ii]) {
1961 						++liquid_levels_want[ii];
1962 						--total_level;
1963 					}
1964 				} else {
1965 					++liquid_levels_want[ii];
1966 					--total_level;
1967 				}
1968 			}
1969 		}
1970 
1971 		for (u16 ir = D_SELF; ir < D_TOP; ++ir) {
1972 			if (total_level < 1) break;
1973 			u16 ii = liquid_random_map[(loopcount+loop_rand+2)%4][ir];
1974 			if (liquid_levels_want[ii] >= 0 &&
1975 				liquid_levels_want[ii] < level_max) {
1976 				++liquid_levels_want[ii];
1977 				--total_level;
1978 			}
1979 		}
1980 
1981 		// fill top block if can
1982 		if (neighbors[D_TOP].l) {
1983 			//infostream<<"compressing to top was="<<liquid_levels_want[D_TOP]<<" add="<<total_level<<std::endl;
1984 			liquid_levels_want[D_TOP] = total_level>level_max_compressed?level_max_compressed:total_level;
1985 			total_level -= liquid_levels_want[D_TOP];
1986 		}
1987 
1988 		if (total_level > 0) { // very rare, compressed only
1989 			//infostream<<"compressing to self was="<<liquid_levels_want[D_SELF]<<" add="<<total_level<<std::endl;
1990 			liquid_levels_want[D_SELF] += total_level;
1991 			total_level = 0;
1992 		}
1993 
1994 		for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1995 			if (    neighbors[ii].i			||
1996 				(liquid_levels_want[ii] >= 0	&&
1997 				 level_max > 1			&&
1998 				 fast_flood			&&
1999 				 p0.Y < water_level		&&
2000 				 initial_size >= 1000		&&
2001 				 ii != D_TOP			&&
2002 				 want_level >= level_max/4	&&
2003 				 can_liquid_same_level >= 5	&&
2004 				 liquid_levels[D_TOP] >= level_max))
2005 					liquid_levels_want[ii] = level_max;
2006 
2007 		/*
2008 		if (total_level > 0) //|| flowed != volume)
2009 			infostream <<" AFTER level=" << (int)total_level
2010 			//<< " flowed="<<flowed<< " volume=" << volume
2011 			<< " max="<<(int)level_max
2012 			<< " wantsame="<<(int)want_level<< " top="
2013 			<< (int)liquid_levels_want[D_TOP]<< " topwas="
2014 			<< (int)liquid_levels[D_TOP]
2015 			<< " bot=" << (int)liquid_levels_want[D_BOTTOM]
2016 			<< " botwas=" << (int)liquid_levels[D_BOTTOM]
2017 			<<std::endl;
2018 		*/
2019 
2020 		//s16 flowed = 0; // for debug
2021 		for (u16 r = 0; r < 7; r++) {
2022 			u16 i = liquid_random_map[(loopcount+loop_rand+3)%4][r];
2023 			if (liquid_levels_want[i] < 0 || !neighbors[i].l)
2024 				continue;
2025 
2026 			//infostream <<" set=" <<i<< " " << PP(neighbors[i].p) << " want="<<(int)liquid_levels_want[i] << " was=" <<(int) liquid_levels[i] << std::endl;
2027 
2028 			/* disabled because brokes constant volume of lava
2029 			u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2030 			if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
2031 				// amount to gain, limited by viscosity
2032 				// must be at least 1 in absolute value
2033 				s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
2034 				if (level_inc < -viscosity || level_inc > viscosity)
2035 					new_node_level = liquid_levels[i] + level_inc/viscosity;
2036 				else if (level_inc < 0)
2037 					new_node_level = liquid_levels[i] - 1;
2038 				else if (level_inc > 0)
2039 					new_node_level = liquid_levels[i] + 1;
2040 			} else {
2041 			*/
2042 
2043 			// last level must flow down on stairs
2044 			if (liquid_levels_want[i] != liquid_levels[i] &&
2045 				liquid_levels[D_TOP] <= 0 && (!neighbors[D_BOTTOM].l || level_max == 1) &&
2046 				liquid_levels_want[i] >= 1 && liquid_levels_want[i] <= 2) {
2047 				for (u16 ir = D_SELF + 1; ir < D_TOP; ++ir) { // only same level
2048 					u16 ii = liquid_random_map[(loopcount+loop_rand+4)%4][ir];
2049 					if (neighbors[ii].l)
2050 						must_reflow_second[neighbors[i].p + liquid_flow_dirs[ii]] = 1;
2051 				}
2052 			}
2053 
2054 			//flowed += liquid_levels_want[i];
2055 			if (liquid_levels[i] == liquid_levels_want[i]) {
2056 				continue;
2057 			}
2058 
2059 			if (neighbors[i].drop) {// && level_max > 1 && total_level >= level_max - 1
2060 				//JMutexAutoLock envlock(m_server->m_env_mutex); // 8(
2061 				m_server->getEnv().getScriptIface()->node_drop(neighbors[i].p, 2);
2062 			}
2063 
2064 			neighbors[i].n.setContent(liquid_kind_flowing);
2065 			neighbors[i].n.setLevel(nodemgr, liquid_levels_want[i], 1);
2066 
2067 			try{
2068 				setNode(neighbors[i].p, neighbors[i].n);
2069 			} catch(InvalidPositionException &e) {
2070 				verbosestream<<"transformLiquidsReal: setNode() failed:"<<neighbors[i].p<<":"<<e.what()<<std::endl;
2071 			}
2072 
2073 			// If node emits light, MapBlock requires lighting update
2074 			// or if node removed
2075 			v3POS blockpos = getNodeBlockPos(neighbors[i].p);
2076 			MapBlock *block = getBlockNoCreateNoEx(blockpos, true); // remove true if light bugs
2077 			if(block != NULL) {
2078 				//modified_blocks[blockpos] = block;
2079 				if(!nodemgr->get(neighbors[i].n).light_propagates || nodemgr->get(neighbors[i].n).light_source) // better to update always
2080 					lighting_modified_blocks.set_try(block->getPos(), block);
2081 			}
2082 			must_reflow[neighbors[i].p] = 1;
2083 
2084 		}
2085 
2086 		//if (total_was!=flowed) infostream<<" flowed "<<flowed<<"/"<<total_was<<std::endl;
2087 		/* //for better relax  only same level
2088 		if (changed)  for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) {
2089 			if (!neighbors[ii].l) continue;
2090 			must_reflow.push_back(p0 + dirs[ii]);
2091 		}*/
2092 		//g_profiler->graphAdd("liquids", 1);
2093 	}
2094 
2095 	u32 ret = loopcount >= initial_size ? 0 : transforming_liquid_size();
2096 	if (ret || loopcount > m_liquid_step_flow)
2097 		m_liquid_step_flow += (m_liquid_step_flow > loopcount ? -1 : 1) * (int)loopcount/10;
2098 	/*
2099 	if (loopcount)
2100 		infostream<<"Map::transformLiquidsReal(): loopcount="<<loopcount<<" initial_size="<<initial_size
2101 		<<" avgflow="<<m_liquid_step_flow
2102 		<<" reflow="<<must_reflow.size()
2103 		<<" reflow_second="<<must_reflow_second.size()
2104 		<<" reflow_third="<<must_reflow_third.size()
2105 		<<" queue="<< transforming_liquid_size()
2106 		<<" per="<< porting::getTimeMs() - (end_ms - max_cycle_ms)
2107 		<<" ret="<<ret<<std::endl;
2108 	*/
2109 
2110 	//JMutexAutoLock lock(m_transforming_liquid_mutex);
2111 
2112 	{
2113 		//TimeTaker timer13("transformLiquidsReal() reflow");
2114 		auto lock = m_transforming_liquid.lock_unique_rec();
2115 		m_transforming_liquid.insert(must_reflow.begin(), must_reflow.end());
2116 		must_reflow.clear();
2117 		m_transforming_liquid.insert(must_reflow_second.begin(), must_reflow_second.end());
2118 		must_reflow_second.clear();
2119 		m_transforming_liquid.insert(must_reflow_third.begin(), must_reflow_third.end());
2120 		must_reflow_third.clear();
2121 	}
2122 
2123 	g_profiler->add("Server: liquids real processed", loopcount);
2124 
2125 	return loopcount;
2126 }
2127 
2128 #define WATER_DROP_BOOST 4
2129 
transformLiquids(Server * m_server,int max_cycle_ms)2130 u32 Map::transformLiquids(Server *m_server, int max_cycle_ms)
2131 {
2132 
2133 	if (g_settings->getBool("liquid_real"))
2134 		return Map::transformLiquidsReal(m_server, max_cycle_ms);
2135 
2136 	INodeDefManager *nodemgr = m_gamedef->ndef();
2137 
2138 	DSTACK(__FUNCTION_NAME);
2139 	//TimeTaker timer("transformLiquids()");
2140 
2141 	//JMutexAutoLock lock(m_transforming_liquid_mutex);
2142 
2143 	u32 loopcount = 0;
2144 	u32 initial_size = transforming_liquid_size();
2145 
2146 	/*if(initial_size != 0)
2147 		infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
2148 
2149 	// list of nodes that due to viscosity have not reached their max level height
2150 	UniqueQueue<v3s16> must_reflow;
2151 
2152 	// List of MapBlocks that will require a lighting update (due to lava)
2153 	//std::map<v3s16, MapBlock*> lighting_modified_blocks;
2154 
2155 	u32 end_ms = porting::getTimeMs() + max_cycle_ms;
2156 
2157 	while(transforming_liquid_size() != 0)
2158 	{
2159 		// This should be done here so that it is done when continue is used
2160 		if(loopcount >= initial_size || porting::getTimeMs() > end_ms)
2161 			break;
2162 		loopcount++;
2163 
2164 		/*
2165 			Get a queued transforming liquid node
2166 		*/
2167 		v3s16 p0 = transforming_liquid_pop();
2168 
2169 		MapNode n0 = getNodeTry(p0);
2170 
2171 		/*
2172 			Collect information about current node
2173 		 */
2174 		s8 liquid_level = -1;
2175 		content_t liquid_kind = CONTENT_IGNORE;
2176 		LiquidType liquid_type = nodemgr->get(n0).liquid_type;
2177 		switch (liquid_type) {
2178 			case LIQUID_SOURCE:
2179 				liquid_level = n0.getLevel(nodemgr);
2180 				liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
2181 				break;
2182 			case LIQUID_FLOWING:
2183 				liquid_level = n0.getLevel(nodemgr);
2184 				liquid_kind = n0.getContent();
2185 				break;
2186 			case LIQUID_NONE:
2187 				// if this is an air node, it *could* be transformed into a liquid. otherwise,
2188 				// continue with the next node.
2189 				if (n0.getContent() != CONTENT_AIR)
2190 					continue;
2191 				liquid_kind = CONTENT_AIR;
2192 				break;
2193 		}
2194 
2195 		/*
2196 			Collect information about the environment
2197 		 */
2198 		const v3s16 *dirs = g_6dirs;
2199 		NodeNeighbor sources[6]; // surrounding sources
2200 		int num_sources = 0;
2201 		NodeNeighbor flows[6]; // surrounding flowing liquid nodes
2202 		int num_flows = 0;
2203 		NodeNeighbor airs[6]; // surrounding air
2204 		int num_airs = 0;
2205 		NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
2206 		int num_neutrals = 0;
2207 		bool flowing_down = false;
2208 		for (u16 i = 0; i < 6; i++) {
2209 			NeighborType nt = NEIGHBOR_SAME_LEVEL;
2210 			switch (i) {
2211 				case 1:
2212 					nt = NEIGHBOR_UPPER;
2213 					break;
2214 				case 4:
2215 					nt = NEIGHBOR_LOWER;
2216 					break;
2217 			}
2218 			v3s16 npos = p0 + dirs[i];
2219 			NodeNeighbor nb = {getNodeTry(npos), nt, npos};
2220 			switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2221 				case LIQUID_NONE:
2222 					if (nb.n.getContent() == CONTENT_AIR) {
2223 						airs[num_airs++] = nb;
2224 						// if the current node is a water source the neighbor
2225 						// should be enqueded for transformation regardless of whether the
2226 						// current node changes or not.
2227 						if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2228 							transforming_liquid_push_back(npos);
2229 						// if the current node happens to be a flowing node, it will start to flow down here.
2230 						if (nb.t == NEIGHBOR_LOWER) {
2231 							flowing_down = true;
2232 						}
2233 					} else {
2234 						neutrals[num_neutrals++] = nb;
2235 					}
2236 					break;
2237 				case LIQUID_SOURCE:
2238 					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2239 					if (liquid_kind == CONTENT_AIR)
2240 						liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2241 					if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2242 						neutrals[num_neutrals++] = nb;
2243 					} else {
2244 						// Do not count bottom source, it will screw things up
2245 						if(dirs[i].Y != -1)
2246 							sources[num_sources++] = nb;
2247 					}
2248 					break;
2249 				case LIQUID_FLOWING:
2250 					// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2251 					if (liquid_kind == CONTENT_AIR)
2252 						liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2253 					if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2254 						neutrals[num_neutrals++] = nb;
2255 					} else {
2256 						flows[num_flows++] = nb;
2257 						if (nb.t == NEIGHBOR_LOWER)
2258 							flowing_down = true;
2259 					}
2260 					break;
2261 			}
2262 		}
2263 		u16 level_max = nodemgr->get(liquid_kind).getMaxLevel(); // source level
2264 		if (level_max <= 1)
2265 			continue;
2266 		level_max -= 1; // source - 1 = max flowing level
2267 		/*
2268 			decide on the type (and possibly level) of the current node
2269 		 */
2270 		content_t new_node_content;
2271 		s8 new_node_level = -1;
2272 		s8 max_node_level = -1;
2273 		if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2274 			// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2275 			// or the flowing alternative of the first of the surrounding sources (if it's air), so
2276 			// it's perfectly safe to use liquid_kind here to determine the new node content.
2277 			//new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2278 			//new_node_content = liquid_kind;
2279 			//max_node_level = level_max + 1;
2280 			new_node_level = level_max + 1;
2281 		} else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2282 			// liquid_kind is set properly, see above
2283 			//new_node_content = liquid_kind;
2284 			new_node_level = level_max;
2285 		} else {
2286 			// no surrounding sources, so get the maximum level that can flow into this node
2287 			for (u16 i = 0; i < num_flows; i++) {
2288 				u8 nb_liquid_level = (flows[i].n.getLevel(nodemgr));
2289 				switch (flows[i].t) {
2290 					case NEIGHBOR_UPPER:
2291 						if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2292 							max_node_level = level_max;
2293 							if (nb_liquid_level + WATER_DROP_BOOST < level_max)
2294 								max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2295 						} else if (nb_liquid_level > max_node_level)
2296 							max_node_level = nb_liquid_level;
2297 						break;
2298 					case NEIGHBOR_LOWER:
2299 						break;
2300 					case NEIGHBOR_SAME_LEVEL:
2301 						if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2302 							nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2303 							max_node_level = nb_liquid_level - 1;
2304 						}
2305 						break;
2306 				}
2307 			}
2308 			u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2309 			if (viscosity > 1 && max_node_level != liquid_level) {
2310 				if (liquid_level < 0)
2311 					liquid_level = 0;
2312 				// amount to gain, limited by viscosity
2313 				// must be at least 1 in absolute value
2314 				s8 level_inc = max_node_level - liquid_level;
2315 				if (level_inc < -viscosity || level_inc > viscosity)
2316 					new_node_level = liquid_level + level_inc/viscosity;
2317 				else if (level_inc < 0)
2318 					new_node_level = liquid_level - 1;
2319 				else if (level_inc > 0)
2320 					new_node_level = liquid_level + 1;
2321 				if (new_node_level != max_node_level)
2322 					must_reflow.push_back(p0);
2323 			} else
2324 				new_node_level = max_node_level;
2325 		}
2326 		new_node_content = liquid_kind;
2327 
2328 		/*
2329 			check if anything has changed. if not, just continue with the next node.
2330 		 */
2331 /*
2332 		if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2333 										 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2334 										 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2335 										 == flowing_down)))
2336 */
2337 		if (liquid_level == new_node_level || new_node_level < 0)
2338 			continue;
2339 
2340 //errorstream << " was="<<(int)liquid_level<<" new="<< (int)new_node_level<< " ncon="<< (int)new_node_content << " flodo="<<(int)flowing_down<< " lmax="<<level_max<< " nameNE="<<nodemgr->get(new_node_content).name<<" nums="<<(int)num_sources<<" wasname="<<nodemgr->get(n0).name<<std::endl;
2341 
2342 		/*
2343 			update the current node
2344 		 */
2345 		MapNode n00 = n0;
2346 		//bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2347 /*
2348 		if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2349 			// set level to last 3 bits, flowing down bit to 4th bit
2350 			n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2351 		} else {
2352 			// set the liquid level and flow bit to 0
2353 			n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2354 		}
2355 */
2356 		n0.setContent(new_node_content);
2357 		n0.setLevel(nodemgr, new_node_level); // set air, flowing, source depend on level
2358 		if (nodemgr->get(n0).liquid_type == LIQUID_FLOWING)
2359 			n0.param2 |= (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00);
2360 
2361 		// Find out whether there is a suspect for this action
2362 		std::string suspect;
2363 		if(m_gamedef->rollback()){
2364 			suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2365 		}
2366 
2367 		if(!suspect.empty()){
2368 			// Blame suspect
2369 			RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2370 			// Get old node for rollback
2371 			RollbackNode rollback_oldnode(this, p0, m_gamedef);
2372 			// Set node
2373 			setNode(p0, n0);
2374 			// Report
2375 			RollbackNode rollback_newnode(this, p0, m_gamedef);
2376 			RollbackAction action;
2377 			action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2378 			m_gamedef->rollback()->reportAction(action);
2379 		} else {
2380 			// Set node
2381 			try{
2382 				setNode(p0, n0);
2383 			}
2384 			catch(InvalidPositionException &e)
2385 			{
2386 				infostream<<"transformLiquids: setNode() failed:"<<PP(p0)<<":"<<e.what()<<std::endl;
2387 			}
2388 		}
2389 		v3s16 blockpos = getNodeBlockPos(p0);
2390 		MapBlock *block = getBlockNoCreateNoEx(blockpos);
2391 		if(block != NULL) {
2392 			//modified_blocks[blockpos] =  block;
2393 			// If new or old node emits light, MapBlock requires lighting update
2394 			if(nodemgr->get(n0).light_source != 0 ||
2395 					nodemgr->get(n00).light_source != 0)
2396 				lighting_modified_blocks.set(block->getPos(), block);
2397 		}
2398 
2399 		/*
2400 			enqueue neighbors for update if neccessary
2401 		 */
2402 		switch (nodemgr->get(n0.getContent()).liquid_type) {
2403 			case LIQUID_SOURCE:
2404 			case LIQUID_FLOWING:
2405 				// make sure source flows into all neighboring nodes
2406 				for (u16 i = 0; i < num_flows; i++)
2407 					if (flows[i].t != NEIGHBOR_UPPER)
2408 						transforming_liquid_push_back(flows[i].p);
2409 				for (u16 i = 0; i < num_airs; i++)
2410 					if (airs[i].t != NEIGHBOR_UPPER)
2411 						transforming_liquid_push_back(airs[i].p);
2412 				break;
2413 			case LIQUID_NONE:
2414 				// this flow has turned to air; neighboring flows might need to do the same
2415 				for (u16 i = 0; i < num_flows; i++)
2416 					transforming_liquid_push_back(flows[i].p);
2417 				break;
2418 		}
2419 	}
2420 
2421 	u32 ret = loopcount >= initial_size ? 0 : transforming_liquid_size();
2422 
2423 	//infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" per="<<timer.getTimerTime()<<" ret="<<ret<<std::endl;
2424 
2425 	while (must_reflow.size() > 0)
2426 		transforming_liquid_push_back(must_reflow.pop_front());
2427 	//updateLighting(lighting_modified_blocks, modified_blocks);
2428 
2429 	g_profiler->add("Server: liquids processed", loopcount);
2430 
2431 	return ret;
2432 }
2433 
getNodeMetadata(v3s16 p)2434 NodeMetadata *Map::getNodeMetadata(v3s16 p)
2435 {
2436 	v3s16 blockpos = getNodeBlockPos(p);
2437 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2438 	MapBlock *block = getBlockNoCreateNoEx(blockpos, false, true);
2439 	if(!block){
2440 		infostream<<"Map::getNodeMetadata(): Need to emerge "
2441 				<<PP(blockpos)<<std::endl;
2442 		block = emergeBlock(blockpos, false);
2443 	}
2444 	if(!block){
2445 		infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2446 				<<std::endl;
2447 		return NULL;
2448 	}
2449 	NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2450 	return meta;
2451 }
2452 
setNodeMetadata(v3s16 p,NodeMetadata * meta)2453 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2454 {
2455 	v3s16 blockpos = getNodeBlockPos(p);
2456 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2457 	MapBlock *block = getBlockNoCreateNoEx(blockpos, false, true);
2458 	if(!block){
2459 		infostream<<"Map::setNodeMetadata(): Need to emerge "
2460 				<<PP(blockpos)<<std::endl;
2461 		block = emergeBlock(blockpos, false);
2462 	}
2463 	if(!block){
2464 		infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2465 				<<std::endl;
2466 		return false;
2467 	}
2468 	block->m_node_metadata.set(p_rel, meta);
2469 	return true;
2470 }
2471 
removeNodeMetadata(v3s16 p)2472 void Map::removeNodeMetadata(v3s16 p)
2473 {
2474 	v3s16 blockpos = getNodeBlockPos(p);
2475 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2476 	MapBlock *block = getBlockNoCreateNoEx(blockpos, false, true);
2477 	if(block == NULL)
2478 	{
2479 		infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2480 				<<std::endl;
2481 		return;
2482 	}
2483 	block->m_node_metadata.remove(p_rel);
2484 }
2485 
getNodeTimer(v3s16 p)2486 NodeTimer Map::getNodeTimer(v3s16 p)
2487 {
2488 	v3s16 blockpos = getNodeBlockPos(p);
2489 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2490 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
2491 	if(!block){
2492 		infostream<<"Map::getNodeTimer(): Need to emerge "
2493 				<<PP(blockpos)<<std::endl;
2494 		block = emergeBlock(blockpos, false);
2495 	}
2496 	if(!block){
2497 		infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2498 				<<std::endl;
2499 		return NodeTimer();
2500 	}
2501 	NodeTimer t = block->m_node_timers.get(p_rel);
2502 	return t;
2503 }
2504 
setNodeTimer(v3s16 p,NodeTimer t)2505 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2506 {
2507 	v3s16 blockpos = getNodeBlockPos(p);
2508 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2509 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
2510 	if(!block){
2511 		infostream<<"Map::setNodeTimer(): Need to emerge "
2512 				<<PP(blockpos)<<std::endl;
2513 		block = emergeBlock(blockpos, false);
2514 	}
2515 	if(!block){
2516 		infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2517 				<<std::endl;
2518 		return;
2519 	}
2520 	block->m_node_timers.set(p_rel, t);
2521 }
2522 
removeNodeTimer(v3s16 p)2523 void Map::removeNodeTimer(v3s16 p)
2524 {
2525 	v3s16 blockpos = getNodeBlockPos(p);
2526 	v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2527 	MapBlock *block = getBlockNoCreateNoEx(blockpos);
2528 	if(block == NULL)
2529 	{
2530 		infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2531 				<<std::endl;
2532 		return;
2533 	}
2534 	block->m_node_timers.remove(p_rel);
2535 }
2536 
getHeat(v3s16 p,bool no_random)2537 s16 Map::getHeat(v3s16 p, bool no_random)
2538 {
2539 	MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2540 	if(block != NULL) {
2541 		s16 value = block->heat;
2542 		// compatibility with minetest
2543 		if (value == HEAT_UNDEFINED)
2544 			return 0;
2545 		return value + (no_random ? 0 : myrand_range(0, 1));
2546 	}
2547 	//errorstream << "No heat for " << p.X<<"," << p.Z << std::endl;
2548 	return 0;
2549 }
2550 
getHumidity(v3s16 p,bool no_random)2551 s16 Map::getHumidity(v3s16 p, bool no_random)
2552 {
2553 	MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p));
2554 	if(block != NULL) {
2555 		s16 value = block->humidity;
2556 		// compatibility with minetest
2557 		if (value == HUMIDITY_UNDEFINED)
2558 			return 0;
2559 		return value + (no_random ? 0 : myrand_range(0, 1));
2560 	}
2561 	//errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl;
2562 	return 0;
2563 }
2564 
2565 /*
2566 	ServerMap
2567 */
ServerMap(std::string savedir,IGameDef * gamedef,EmergeManager * emerge,Circuit * circuit)2568 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge, Circuit* circuit):
2569 	Map(gamedef, circuit),
2570 	m_emerge(emerge),
2571 	m_map_metadata_changed(true)
2572 {
2573 	verbosestream<<__FUNCTION_NAME<<std::endl;
2574 
2575 	/*
2576 		Try to load map; if not found, create a new one.
2577 	*/
2578 
2579 	// Determine which database backend to use
2580 	std::string conf_path = savedir + DIR_DELIM + "world.mt";
2581 	Settings conf;
2582 	bool succeeded = conf.readConfigFile(conf_path.c_str());
2583 	if (!succeeded || !conf.exists("backend")) {
2584 		// fall back to sqlite3
2585 		dbase = new Database_SQLite3(this, savedir);
2586 		conf.set("backend", "sqlite3");
2587 	} else {
2588 		std::string backend = conf.get("backend");
2589 		if (backend == "dummy")
2590 			dbase = new Database_Dummy(this);
2591 		else if (backend == "sqlite3")
2592 			dbase = new Database_SQLite3(this, savedir);
2593 		#if USE_LEVELDB
2594 		else if (backend == "leveldb")
2595 			dbase = new Database_LevelDB(this, savedir);
2596 		#endif
2597 		#if USE_REDIS
2598 		else if (backend == "redis")
2599 			dbase = new Database_Redis(this, savedir);
2600 		#endif
2601 		else
2602 			throw BaseException("Unknown map backend");
2603 	}
2604 
2605 	m_savedir = savedir;
2606 	m_map_saving_enabled = false;
2607 	m_map_loading_enabled = true;
2608 
2609 	try
2610 	{
2611 		// If directory exists, check contents and load if possible
2612 		if(fs::PathExists(m_savedir))
2613 		{
2614 			// If directory is empty, it is safe to save into it.
2615 			if(fs::GetDirListing(m_savedir).size() == 0)
2616 			{
2617 				infostream<<"ServerMap: Empty save directory is valid."
2618 						<<std::endl;
2619 				m_map_saving_enabled = true;
2620 			}
2621 			else
2622 			{
2623 				try{
2624 					// Load map metadata (seed, chunksize)
2625 					loadMapMeta();
2626 				}
2627 				catch(SettingNotFoundException &e){
2628 					infostream<<"ServerMap:  Some metadata not found."
2629 							  <<" Using default settings."<<std::endl;
2630 				}
2631 				catch(FileNotGoodException &e){
2632 					infostream<<"WARNING: Could not load map metadata"
2633 							//<<" Disabling chunk-based generator."
2634 							<<std::endl;
2635 					//m_chunksize = 0;
2636 				}
2637 
2638 				infostream<<"ServerMap: Successfully loaded map "
2639 						<<"metadata from "<<savedir
2640 						<<", assuming valid save directory."
2641 						<<" seed="<< m_emerge->params.seed <<"."
2642 						<<std::endl;
2643 
2644 				m_map_saving_enabled = true;
2645 				// Map loaded, not creating new one
2646 				return;
2647 			}
2648 		}
2649 		// If directory doesn't exist, it is safe to save to it
2650 		else{
2651 			m_map_saving_enabled = true;
2652 		}
2653 	}
2654 	catch(std::exception &e)
2655 	{
2656 		infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2657 				<<", exception: "<<e.what()<<std::endl;
2658 		infostream<<"Please remove the map or fix it."<<std::endl;
2659 		infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2660 	}
2661 
2662 	infostream<<"Initializing new map."<<std::endl;
2663 
2664 	// Initially write whole map
2665 	save(MOD_STATE_CLEAN);
2666 }
2667 
~ServerMap()2668 ServerMap::~ServerMap()
2669 {
2670 	verbosestream<<__FUNCTION_NAME<<std::endl;
2671 
2672 	try
2673 	{
2674 			// Save only changed parts
2675 			save(MOD_STATE_WRITE_AT_UNLOAD);
2676 	}
2677 	catch(std::exception &e)
2678 	{
2679 		infostream<<"ServerMap: Failed to save map to "<<m_savedir
2680 				<<", exception: "<<e.what()<<std::endl;
2681 	}
2682 
2683 	/*
2684 		Close database if it was opened
2685 	*/
2686 	delete dbase;
2687 
2688 }
2689 
getSeed()2690 u64 ServerMap::getSeed()
2691 {
2692 	return m_emerge->params.seed;
2693 }
2694 
getWaterLevel()2695 s16 ServerMap::getWaterLevel()
2696 {
2697 	return m_emerge->params.water_level;
2698 }
2699 
initBlockMake(BlockMakeData * data,v3s16 blockpos)2700 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2701 {
2702 	bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2703 	EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2704 
2705 	s16 chunksize = m_emerge->params.chunksize;
2706 	s16 coffset = -chunksize / 2;
2707 	v3s16 chunk_offset(coffset, coffset, coffset);
2708 	v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2709 	v3s16 blockpos_min = blockpos_div * chunksize;
2710 	v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2711 	blockpos_min += chunk_offset;
2712 	blockpos_max += chunk_offset;
2713 
2714 	v3s16 extra_borders(1,1,1);
2715 
2716 	// Do nothing if not inside limits (+-1 because of neighbors)
2717 	if(blockpos_over_limit(blockpos_min - extra_borders) ||
2718 		blockpos_over_limit(blockpos_max + extra_borders))
2719 		return false;
2720 
2721 	data->seed = m_emerge->params.seed;
2722 	data->blockpos_min = blockpos_min;
2723 	data->blockpos_max = blockpos_max;
2724 	data->blockpos_requested = blockpos;
2725 	data->nodedef = m_gamedef->ndef();
2726 
2727 	/*
2728 		Create the whole area of this and the neighboring blocks
2729 	*/
2730 	{
2731 		//TimeTaker timer("initBlockMake() create area");
2732 
2733 		for(s16 x=blockpos_min.X-extra_borders.X;
2734 				x<=blockpos_max.X+extra_borders.X; x++)
2735 		for(s16 z=blockpos_min.Z-extra_borders.Z;
2736 				z<=blockpos_max.Z+extra_borders.Z; z++)
2737 		{
2738 			for(s16 y=blockpos_min.Y-extra_borders.Y;
2739 					y<=blockpos_max.Y+extra_borders.Y; y++)
2740 			{
2741 				v3s16 p(x,y,z);
2742 				//MapBlock *block = createBlock(p);
2743 				// 1) get from memory, 2) load from disk
2744 				MapBlock *block = emergeBlock(p, false);
2745 				// 3) create a blank one
2746 				if(block == NULL)
2747 				{
2748 					block = createBlock(p);
2749 
2750 					/*
2751 						Block gets sunlight if this is true.
2752 
2753 						Refer to the map generator heuristics.
2754 					*/
2755 					bool ug = m_emerge->isBlockUnderground(p);
2756 					block->setIsUnderground(ug);
2757 				}
2758 
2759 				// Lighting will not be valid after make_chunk is called
2760 				block->setLightingExpired(true);
2761 				// Lighting will be calculated
2762 				//block->setLightingExpired(false);
2763 			}
2764 		}
2765 	}
2766 
2767 	/*
2768 		Now we have a big empty area.
2769 
2770 		Make a ManualMapVoxelManipulator that contains this and the
2771 		neighboring blocks
2772 	*/
2773 
2774 	// The area that contains this block and it's neighbors
2775 	v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2776 	v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2777 
2778 	data->vmanip = new ManualMapVoxelManipulator(this);
2779 	//data->vmanip->setMap(this);
2780 
2781 	// Add the area
2782 	{
2783 		//TimeTaker timer("initBlockMake() initialEmerge");
2784 		data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2785 	}
2786 
2787 	// Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2788 /*	for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2789 		for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2790 			for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2791 				core::map<v3s16, u8>::Node *n;
2792 				n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2793 				if (n == NULL)
2794 					continue;
2795 				u8 flags = n->getValue();
2796 				flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2797 				n->setValue(flags);
2798 			}
2799 		}
2800 	}*/
2801 
2802 	// Data is ready now.
2803 	return true;
2804 }
2805 
finishBlockMake(BlockMakeData * data,std::map<v3s16,MapBlock * > & changed_blocks)2806 void ServerMap::finishBlockMake(BlockMakeData *data,
2807 		std::map<v3s16, MapBlock*> &changed_blocks)
2808 {
2809 	v3s16 blockpos_min = data->blockpos_min;
2810 	v3s16 blockpos_max = data->blockpos_max;
2811 	v3s16 blockpos_requested = data->blockpos_requested;
2812 	/*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2813 			<<blockpos_requested.Y<<","
2814 			<<blockpos_requested.Z<<")"<<std::endl;*/
2815 
2816 	v3s16 extra_borders(1,1,1);
2817 
2818 	bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2819 
2820 	/*infostream<<"Resulting vmanip:"<<std::endl;
2821 	data->vmanip.print(infostream);*/
2822 
2823 	// Make sure affected blocks are loaded
2824 	for(s16 x=blockpos_min.X-extra_borders.X;
2825 			x<=blockpos_max.X+extra_borders.X; x++)
2826 	for(s16 z=blockpos_min.Z-extra_borders.Z;
2827 			z<=blockpos_max.Z+extra_borders.Z; z++)
2828 	for(s16 y=blockpos_min.Y-extra_borders.Y;
2829 			y<=blockpos_max.Y+extra_borders.Y; y++)
2830 	{
2831 		v3s16 p(x, y, z);
2832 		// Load from disk if not already in memory
2833 		emergeBlock(p, false);
2834 	}
2835 
2836 	/*
2837 		Blit generated stuff to map
2838 		NOTE: blitBackAll adds nearly everything to changed_blocks
2839 	*/
2840 	{
2841 		// 70ms @cs=8
2842 		//TimeTaker timer("finishBlockMake() blitBackAll");
2843 		data->vmanip->blitBackAll(&changed_blocks, false);
2844 	}
2845 
2846 	EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2847 
2848 	/*
2849 		Do stuff in central blocks
2850 	*/
2851 
2852 	/*
2853 		Update lighting
2854 	*/
2855 	{
2856 #if 0
2857 		TimeTaker t("finishBlockMake lighting update");
2858 
2859 		core::map<v3s16, MapBlock*> lighting_update_blocks;
2860 
2861 		// Center blocks
2862 		for(s16 x=blockpos_min.X-extra_borders.X;
2863 				x<=blockpos_max.X+extra_borders.X; x++)
2864 		for(s16 z=blockpos_min.Z-extra_borders.Z;
2865 				z<=blockpos_max.Z+extra_borders.Z; z++)
2866 		for(s16 y=blockpos_min.Y-extra_borders.Y;
2867 				y<=blockpos_max.Y+extra_borders.Y; y++)
2868 		{
2869 			v3s16 p(x, y, z);
2870 			MapBlock *block = getBlockNoCreateNoEx(p);
2871 			assert(block);
2872 			lighting_update_blocks.insert(block->getPos(), block);
2873 		}
2874 
2875 		updateLighting(lighting_update_blocks, changed_blocks);
2876 #endif
2877 
2878 		/*
2879 			Set lighting to non-expired state in all of them.
2880 			This is cheating, but it is not fast enough if all of them
2881 			would actually be updated.
2882 		*/
2883 		for(s16 x=blockpos_min.X-extra_borders.X;
2884 				x<=blockpos_max.X+extra_borders.X; x++)
2885 		for(s16 z=blockpos_min.Z-extra_borders.Z;
2886 				z<=blockpos_max.Z+extra_borders.Z; z++)
2887 		for(s16 y=blockpos_min.Y-extra_borders.Y;
2888 				y<=blockpos_max.Y+extra_borders.Y; y++)
2889 		{
2890 			v3s16 p(x, y, z);
2891 			MapBlock * block = getBlockNoCreateNoEx(p);
2892 			if (block == NULL)
2893 				continue;
2894 			block->setLightingExpired(false);
2895 		}
2896 
2897 #if 0
2898 		if(enable_mapgen_debug_info == false)
2899 			t.stop(true); // Hide output
2900 #endif
2901 	}
2902 
2903 	/*
2904 		Go through changed blocks
2905 	*/
2906 	for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2907 			i != changed_blocks.end(); ++i)
2908 	{
2909 		MapBlock *block = i->second;
2910 		if (!block)
2911 			continue;
2912 		/*
2913 			Update day/night difference cache of the MapBlocks
2914 		*/
2915 		block->expireDayNightDiff();
2916 		/*
2917 			Set block as modified
2918 		*/
2919 
2920 		if (g_settings->getBool("save_generated_block"))
2921 		block->raiseModified(MOD_STATE_WRITE_NEEDED,
2922 				"finishBlockMake expireDayNightDiff");
2923 
2924 	}
2925 
2926 	/*
2927 		Set central blocks as generated
2928 	*/
2929 	for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2930 	for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2931 	for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2932 	{
2933 		v3s16 p(x, y, z);
2934 		MapBlock *block = getBlockNoCreateNoEx(p);
2935 		if (!block)
2936 			continue;
2937 		block->setGenerated(true);
2938 	}
2939 
2940 	/*
2941 		Save changed parts of map
2942 		NOTE: Will be saved later.
2943 	*/
2944 	//save(MOD_STATE_WRITE_AT_UNLOAD);
2945 
2946 	/*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2947 			<<","<<blockpos_requested.Y<<","
2948 			<<blockpos_requested.Z<<")"<<std::endl;*/
2949 
2950 	/*
2951 		Update weather data in blocks
2952 	*/
2953 	ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
2954 	for(s16 x=blockpos_min.X-extra_borders.X;x<=blockpos_max.X+extra_borders.X; x++)
2955 	for(s16 z=blockpos_min.Z-extra_borders.Z;z<=blockpos_max.Z+extra_borders.Z; z++)
2956 	for(s16 y=blockpos_min.Y-extra_borders.Y;y<=blockpos_max.Y+extra_borders.Y; y++) {
2957 		v3POS p(x, y, z);
2958 		MapBlock *block = getBlockNoCreateNoEx(p);
2959 		if (!block)
2960 			continue;
2961 		updateBlockHeat(senv, p * MAP_BLOCKSIZE, block);
2962 		updateBlockHumidity(senv, p * MAP_BLOCKSIZE, block);
2963 	}
2964 
2965 #if 0
2966 	if(enable_mapgen_debug_info)
2967 	{
2968 		/*
2969 			Analyze resulting blocks
2970 		*/
2971 		/*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2972 		for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2973 		for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2974 		for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2975 		for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2976 		for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2977 		{
2978 			v3s16 p = v3s16(x,y,z);
2979 			MapBlock *block = getBlockNoCreateNoEx(p);
2980 			char spos[20];
2981 			snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2982 			infostream<<"Generated "<<spos<<": "
2983 					<<analyze_block(block)<<std::endl;
2984 		}
2985 	}
2986 #endif
2987 
2988 	MapBlock * block = getBlockNoCreateNoEx(blockpos_requested, false, true);
2989 	if(!block) {
2990 		errorstream<<"finishBlockMake(): created NULL block at "<<PP(blockpos_requested)<<std::endl;
2991 	}
2992 
2993 }
2994 
createBlock(v3s16 p)2995 MapBlock * ServerMap::createBlock(v3s16 p)
2996 {
2997 	DSTACKF("%s: p=(%d,%d,%d)",
2998 			__FUNCTION_NAME, p.X, p.Y, p.Z);
2999 
3000 	/*
3001 		Do not create over-limit
3002 	*/
3003 	if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3004 	|| p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3005 	|| p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3006 	|| p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3007 	|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3008 	|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3009 		throw InvalidPositionException("createBlock(): pos. over limit");
3010 
3011 	MapBlock *block = this->getBlockNoCreateNoEx(p, false, true);
3012 	if(block)
3013 	{
3014 		if(block->isDummy())
3015 			block->unDummify();
3016 		return block;
3017 	}
3018 	// Create blank
3019 	block = this->createBlankBlock(p);
3020 
3021 	return block;
3022 }
3023 
emergeBlock(v3s16 p,bool create_blank)3024 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
3025 {
3026 	DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
3027 			__FUNCTION_NAME,
3028 			p.X, p.Y, p.Z, create_blank);
3029 	{
3030 		MapBlock *block = getBlockNoCreateNoEx(p, false, true);
3031 		if(block && block->isDummy() == false)
3032 		{
3033 			return block;
3034 		}
3035 	}
3036 
3037 	if (!m_map_loading_enabled)
3038 		return nullptr;
3039 
3040 	{
3041 		MapBlock *block = loadBlock(p);
3042 		if(block)
3043 			return block;
3044 	}
3045 
3046 	if (create_blank) {
3047 		return this->createBlankBlock(p);
3048 	}
3049 
3050 	return NULL;
3051 }
3052 
getBlockOrEmerge(v3s16 p3d)3053 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
3054 {
3055 	MapBlock *block = getBlockNoCreateNoEx(p3d);
3056 	if (block == NULL && m_map_loading_enabled)
3057 		m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
3058 
3059 	return block;
3060 }
3061 
prepareBlock(MapBlock * block)3062 void ServerMap::prepareBlock(MapBlock *block) {
3063 	ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
3064 
3065 	// Calculate weather conditions
3066 	//block->heat_last_update     = 0;
3067 	//block->humidity_last_update = 0;
3068 	v3POS p = block->getPos() *  MAP_BLOCKSIZE;
3069 	updateBlockHeat(senv, p, block);
3070 	updateBlockHumidity(senv, p, block);
3071 }
3072 
3073 // N.B.  This requires no synchronization, since data will not be modified unless
3074 // the VoxelManipulator being updated belongs to the same thread.
updateVManip(v3s16 pos)3075 void ServerMap::updateVManip(v3s16 pos)
3076 {
3077 	Mapgen *mg = m_emerge->getCurrentMapgen();
3078 	if (!mg)
3079 		return;
3080 
3081 	ManualMapVoxelManipulator *vm = mg->vm;
3082 	if (!vm)
3083 		return;
3084 
3085 	if (!vm->m_area.contains(pos))
3086 		return;
3087 
3088 	s32 idx = vm->m_area.index(pos);
3089 	vm->m_data[idx] = getNodeNoEx(pos);
3090 	vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
3091 
3092 	vm->m_is_dirty = true;
3093 }
3094 
3095 /**
3096  * Get the ground level by searching for a non CONTENT_AIR node in a column from top to bottom
3097  */
findGroundLevel(v2POS p2d,bool cacheBlocks)3098 s16 ServerMap::findGroundLevel(v2POS p2d, bool cacheBlocks)
3099 {
3100 
3101 	POS level;
3102 
3103 	// The reference height is the original mapgen height
3104 	POS referenceHeight = m_emerge->getGroundLevelAtPoint(p2d);
3105 	POS maxSearchHeight =  63 + referenceHeight;
3106 	POS minSearchHeight = -63 + referenceHeight;
3107 	v3POS probePosition(p2d.X, maxSearchHeight, p2d.Y);
3108 	v3POS blockPosition = getNodeBlockPos(probePosition);
3109 	v3POS prevBlockPosition = blockPosition;
3110 
3111 	// Cache the block to be inspected.
3112 	if(cacheBlocks) {
3113 		emergeBlock(blockPosition, true);
3114 	}
3115 
3116 	// Probes the nodes in the given column
3117 	for(; probePosition.Y > minSearchHeight; probePosition.Y--)
3118 	{
3119 		if(cacheBlocks) {
3120 			// Calculate the block position of the given node
3121 			blockPosition = getNodeBlockPos(probePosition);
3122 
3123 			// If the node is in an different block, cache it
3124 			if(blockPosition != prevBlockPosition) {
3125 				emergeBlock(blockPosition, true);
3126 				prevBlockPosition = blockPosition;
3127 			}
3128 		}
3129 
3130 		MapNode node = getNodeNoEx(probePosition);
3131 		if (node.getContent() != CONTENT_IGNORE &&
3132 		    node.getContent() != CONTENT_AIR) {
3133 			break;
3134 		}
3135 	}
3136 
3137 	// Could not determine the ground. Use map generator noise functions.
3138 	if(probePosition.Y == minSearchHeight) {
3139 		level = referenceHeight;
3140 	} else {
3141 		level = probePosition.Y;
3142 	}
3143 
3144 	return level;
3145 }
3146 
createDirs(std::string path)3147 void ServerMap::createDirs(std::string path)
3148 {
3149 	if(fs::CreateAllDirs(path) == false)
3150 	{
3151 		errorstream<<DTIME<<"ServerMap: Failed to create directory "
3152 				<<"\""<<path<<"\""<<std::endl;
3153 		throw BaseException("ServerMap failed to create directory");
3154 	}
3155 }
3156 
save(ModifiedState save_level,bool breakable)3157 s32 ServerMap::save(ModifiedState save_level, bool breakable)
3158 {
3159 	DSTACK(__FUNCTION_NAME);
3160 	if(m_map_saving_enabled == false)
3161 	{
3162 		infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3163 		return 0;
3164 	}
3165 
3166 	if(save_level == MOD_STATE_CLEAN)
3167 		infostream<<"ServerMap: Saving whole map, this can take time."
3168 				<<std::endl;
3169 
3170 	if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3171 	{
3172 		saveMapMeta();
3173 	}
3174 
3175 	// Profile modified reasons
3176 	Profiler modprofiler;
3177 
3178 	u32 block_count = 0;
3179 	u32 block_count_all = 0; // Number of blocks in memory
3180 
3181 	// Don't do anything with sqlite unless something is really saved
3182 	bool save_started = false;
3183 	u32 n = 0, calls = 0, end_ms = porting::getTimeMs() + u32(1000 * g_settings->getFloat("dedicated_server_step"));
3184 	if (!breakable)
3185 		m_blocks_save_last = 0;
3186 
3187 	{
3188 		auto lock = breakable ? m_blocks.try_lock_shared_rec() : m_blocks.lock_shared_rec();
3189 		if (!lock->owns_lock())
3190 			return m_blocks_save_last;
3191 
3192 		for(auto &jr : m_blocks)
3193 		{
3194 			if (n++ < m_blocks_save_last)
3195 				continue;
3196 			else
3197 				m_blocks_save_last = 0;
3198 			++calls;
3199 
3200 			MapBlock *block = jr.second.get();
3201 
3202 			if (!block)
3203 				continue;
3204 
3205 			block_count_all++;
3206 
3207 			if(block->getModified() >= (u32)save_level)
3208 			{
3209 				// Lazy beginSave()
3210 				if(!save_started){
3211 					beginSave();
3212 					save_started = true;
3213 				}
3214 
3215 				//modprofiler.add(block->getModifiedReason(), 1);
3216 				auto lock = breakable ? block->try_lock_unique_rec() : block->lock_unique_rec();
3217 				if (!lock->owns_lock())
3218 					continue;
3219 
3220 				saveBlock(block);
3221 				block_count++;
3222 
3223 				/*infostream<<"ServerMap: Written block ("
3224 						<<block->getPos().X<<","
3225 						<<block->getPos().Y<<","
3226 						<<block->getPos().Z<<")"
3227 						<<std::endl;*/
3228 			}
3229 		if (breakable && porting::getTimeMs() > end_ms) {
3230 				m_blocks_save_last = n;
3231 				break;
3232 		}
3233 	}
3234 	}
3235 	if (!calls)
3236 		m_blocks_save_last = 0;
3237 
3238 	if(save_started)
3239 		endSave();
3240 
3241 	/*
3242 		Only print if something happened or saved whole map
3243 	*/
3244 	if(/*save_level == MOD_STATE_CLEAN
3245 			||*/ block_count != 0)
3246 	{
3247 		infostream<<"ServerMap: Written: "
3248 				<<block_count<<"/"<<block_count_all<<" blocks from "
3249 				<<m_blocks.size();
3250 		if (m_blocks_save_last)
3251 			infostream<<" to "<< m_blocks_save_last;
3252 		infostream<<std::endl;
3253 		PrintInfo(infostream); // ServerMap/ClientMap:
3254 		//infostream<<"Blocks modified by: "<<std::endl;
3255 		modprofiler.print(infostream);
3256 	}
3257 	return m_blocks_save_last;
3258 }
3259 
listAllLoadableBlocks(std::list<v3s16> & dst)3260 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3261 {
3262 	dbase->listAllLoadableBlocks(dst);
3263 }
3264 
listAllLoadedBlocks(std::list<v3s16> & dst)3265 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3266 {
3267 	auto lock = m_blocks.lock_shared_rec();
3268 	for(auto & i : m_blocks)
3269 		dst.push_back(i.second->getPos());
3270 }
3271 
saveMapMeta()3272 void ServerMap::saveMapMeta()
3273 {
3274 	DSTACK(__FUNCTION_NAME);
3275 
3276 	/*infostream<<"ServerMap::saveMapMeta(): "
3277 			<<"seed="<<m_seed
3278 			<<std::endl;*/
3279 
3280 	createDirs(m_savedir);
3281 
3282 	std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3283 	std::ostringstream ss(std::ios_base::binary);
3284 
3285 	Settings params;
3286 
3287 	m_emerge->saveParamsToSettings(&params);
3288 	params.writeLines(ss);
3289 
3290 	ss<<"[end_of_params]\n";
3291 
3292 	if(!fs::safeWriteToFile(fullpath, ss.str()))
3293 	{
3294 		infostream<<"ERROR: ServerMap::saveMapMeta(): "
3295 				<<"could not write "<<fullpath<<std::endl;
3296 		throw FileNotGoodException("Cannot save chunk metadata");
3297 	}
3298 
3299 	m_map_metadata_changed = false;
3300 }
3301 
loadMapMeta()3302 void ServerMap::loadMapMeta()
3303 {
3304 	DSTACK(__FUNCTION_NAME);
3305 
3306 	std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3307 	std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3308 	if (!is.good()) {
3309 		errorstream << "ServerMap::loadMapMeta(): "
3310 				<< "could not open " << fullpath << std::endl;
3311 		throw FileNotGoodException("Cannot open map metadata");
3312 	}
3313 
3314 	Settings params;
3315 
3316 	if (!params.parseConfigLines(is, "[end_of_params]")) {
3317 		throw SerializationError("ServerMap::loadMapMeta(): "
3318 				"[end_of_params] not found!");
3319 	}
3320 
3321 	m_emerge->loadParamsFromSettings(&params);
3322 
3323 	verbosestream << "ServerMap::loadMapMeta(): seed="
3324 		<< m_emerge->params.seed << std::endl;
3325 }
3326 
beginSave()3327 void ServerMap::beginSave()
3328 {
3329 	dbase->beginSave();
3330 }
3331 
endSave()3332 void ServerMap::endSave()
3333 {
3334 	dbase->endSave();
3335 }
3336 
saveBlock(MapBlock * block)3337 bool ServerMap::saveBlock(MapBlock *block)
3338 {
3339 	return saveBlock(block, dbase);
3340 }
3341 
saveBlock(MapBlock * block,Database * db)3342 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3343 {
3344 	v3s16 p3d = block->getPos();
3345 
3346 	// Dummy blocks are not written
3347 	if (block->isDummy()) {
3348 		errorstream << "WARNING: saveBlock: Not writing dummy block "
3349 			<< PP(p3d) << std::endl;
3350 		return true;
3351 	}
3352 
3353 	// Format used for writing
3354 	u8 version = SER_FMT_VER_HIGHEST_WRITE;
3355 
3356 	/*
3357 		[0] u8 serialization version
3358 		[1] data
3359 	*/
3360 	std::ostringstream o(std::ios_base::binary);
3361 	o.write((char*) &version, 1);
3362 	block->serialize(o, version, true);
3363 
3364 	std::string data = o.str();
3365 	bool ret = db->saveBlock(p3d, data);
3366 	if(ret) {
3367 		// We just wrote it to the disk so clear modified flag
3368 		block->resetModified();
3369 	}
3370 	return ret;
3371 }
3372 
loadBlock(v3s16 p3d)3373 MapBlock * ServerMap::loadBlock(v3s16 p3d)
3374 {
3375 	DSTACK(__FUNCTION_NAME);
3376 	ScopeProfiler sp(g_profiler, "ServerMap::loadBlock");
3377 	const auto sector = this;
3378 	auto blob = dbase->loadBlock(p3d);
3379 	if(!blob.length())
3380 		return nullptr;
3381 
3382 	try {
3383 		std::istringstream is(blob, std::ios_base::binary);
3384 
3385 		u8 version = SER_FMT_VER_INVALID;
3386 		is.read((char*)&version, 1);
3387 
3388 		if(is.fail())
3389 			throw SerializationError("ServerMap::loadBlock(): Failed"
3390 					" to read MapBlock version");
3391 
3392 		/*u32 block_size = MapBlock::serializedLength(version);
3393 		SharedBuffer<u8> data(block_size);
3394 		is.read((char*)*data, block_size);*/
3395 
3396 		// This will always return a sector because we're the server
3397 		//MapSector *sector = emergeSector(p2d);
3398 
3399 		MapBlock *block = NULL;
3400 		bool created_new = false;
3401 		block = sector->getBlockNoCreateNoEx(p3d);
3402 		if(block == NULL)
3403 		{
3404 			block = sector->createBlankBlockNoInsert(p3d);
3405 			created_new = true;
3406 		}
3407 
3408 		// Read basic data
3409 		block->deSerialize(is, version, true);
3410 
3411 		// If it's a new block, insert it to the map
3412 		if(created_new)
3413 			sector->insertBlock(block);
3414 
3415 		// We just loaded it from, so it's up-to-date.
3416 		block->resetModified();
3417 
3418 		if (block->getLightingExpired()) {
3419 			infostream<<"Loaded block with exiried lighting. (maybe sloooow appear), try recalc " << p3d<<std::endl;
3420 			lighting_modified_blocks.set(p3d, nullptr);
3421 		}
3422 
3423 		return block;
3424 	}
3425 	catch(SerializationError &e)
3426 	{
3427 		errorstream<<"Invalid block data in database"
3428 				<<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3429 				<<" (SerializationError): "<<e.what()<<std::endl;
3430 
3431 		// TODO: Block should be marked as invalid in memory so that it is
3432 		// not touched but the game can run
3433 
3434 		if(g_settings->getBool("ignore_world_load_errors")){
3435 			errorstream<<"Ignoring block load error. Duck and cover! "
3436 					<<"(ignore_world_load_errors)"<<std::endl;
3437 		} else {
3438 			throw SerializationError("Invalid block data in database");
3439 			//assert(0);
3440 		}
3441 	}
3442 	return nullptr;
3443 }
3444 
PrintInfo(std::ostream & out)3445 void ServerMap::PrintInfo(std::ostream &out)
3446 {
3447 	out<<"ServerMap: ";
3448 }
3449 
updateBlockHeat(ServerEnvironment * env,v3POS p,MapBlock * block,std::map<v3POS,s16> * cache)3450 s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3POS p, MapBlock *block, std::map<v3POS, s16> * cache)
3451 {
3452 	auto bp = getNodeBlockPos(p);
3453 	auto gametime = env->getGameTime();
3454 	if (block) {
3455 		if (gametime < block->heat_last_update)
3456 			return block->heat + myrand_range(0, 1);
3457 	} else if (!cache) {
3458 		block = getBlockNoCreateNoEx(bp, true);
3459 	}
3460 	if (cache && cache->count(bp))
3461 		return cache->at(bp) + myrand_range(0, 1);
3462 
3463 	auto value = m_emerge->biomemgr->calcBlockHeat(p, getSeed(),
3464 			env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed(), env->m_use_weather);
3465 
3466 	if(block) {
3467 		block->heat = value;
3468 		block->heat_last_update = env->m_use_weather ? gametime + 30 : -1;
3469 	}
3470 	if (cache)
3471 		(*cache)[bp] = value;
3472 	return value + myrand_range(0, 1);
3473 }
3474 
updateBlockHumidity(ServerEnvironment * env,v3POS p,MapBlock * block,std::map<v3POS,s16> * cache)3475 s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3POS p, MapBlock *block, std::map<v3POS, s16> * cache)
3476 {
3477 	auto bp = getNodeBlockPos(p);
3478 	auto gametime = env->getGameTime();
3479 	if (block) {
3480 		if (gametime < block->humidity_last_update)
3481 			return block->humidity + myrand_range(0, 1);
3482 	} else if (!cache) {
3483 		block = getBlockNoCreateNoEx(bp, true);
3484 	}
3485 	if (cache && cache->count(bp))
3486 		return cache->at(bp) + myrand_range(0, 1);
3487 
3488 	auto value = m_emerge->biomemgr->calcBlockHumidity(p, getSeed(),
3489 			env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed(), env->m_use_weather);
3490 
3491 	if(block) {
3492 		block->humidity = value;
3493 		block->humidity_last_update = env->m_use_weather ? gametime + 30 : -1;
3494 	}
3495 	if (cache)
3496 		(*cache)[bp] = value;
3497 	return value + myrand_range(0, 1);
3498 }
3499 
getSurface(v3s16 basepos,int searchup,bool walkable_only)3500 int ServerMap::getSurface(v3s16 basepos, int searchup, bool walkable_only) {
3501 
3502 	s16 max = MYMIN(searchup + basepos.Y,0x7FFF);
3503 
3504 	MapNode last_node = getNodeNoEx(basepos);
3505 	MapNode node = last_node;
3506 	v3s16 runpos = basepos;
3507 	INodeDefManager *nodemgr = m_gamedef->ndef();
3508 
3509 	bool last_was_walkable = nodemgr->get(node).walkable;
3510 
3511 	while ((runpos.Y < max) && (node.param0 != CONTENT_AIR)) {
3512 		runpos.Y += 1;
3513 		last_node = node;
3514 		node = getNodeNoEx(runpos);
3515 
3516 		if (!walkable_only) {
3517 			if ((last_node.param0 != CONTENT_AIR) &&
3518 				(last_node.param0 != CONTENT_IGNORE) &&
3519 				(node.param0 == CONTENT_AIR)) {
3520 				return runpos.Y;
3521 			}
3522 		}
3523 		else {
3524 			bool is_walkable = nodemgr->get(node).walkable;
3525 
3526 			if (last_was_walkable && (!is_walkable)) {
3527 				return runpos.Y;
3528 			}
3529 			last_was_walkable = is_walkable;
3530 		}
3531 	}
3532 
3533 	return basepos.Y -1;
3534 }
3535 
ManualMapVoxelManipulator(Map * map)3536 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3537 		VoxelManipulator(),
3538 		m_is_dirty(false),
3539 		m_create_area(false),
3540 		m_map(map)
3541 {
3542 }
3543 
~ManualMapVoxelManipulator()3544 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3545 {
3546 }
3547 
initialEmerge(v3s16 blockpos_min,v3s16 blockpos_max,bool load_if_inexistent)3548 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3549 						v3s16 blockpos_max, bool load_if_inexistent)
3550 {
3551 	TimeTaker timer1("initialEmerge");
3552 
3553 	// Units of these are MapBlocks
3554 	v3s16 p_min = blockpos_min;
3555 	v3s16 p_max = blockpos_max;
3556 
3557 	VoxelArea block_area_nodes
3558 			(p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3559 
3560 	u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3561 	if(size_MB >= 1)
3562 	{
3563 		infostream<<"initialEmerge: area: ";
3564 		block_area_nodes.print(infostream);
3565 		infostream<<" ("<<size_MB<<"MB)";
3566 		infostream<<" load_if_inexistent="<<load_if_inexistent;
3567 		infostream<<std::endl;
3568 	}
3569 
3570 	addArea(block_area_nodes);
3571 
3572 	for(s32 z=p_min.Z; z<=p_max.Z; z++)
3573 	for(s32 y=p_min.Y; y<=p_max.Y; y++)
3574 	for(s32 x=p_min.X; x<=p_max.X; x++)
3575 	{
3576 		u8 flags = 0;
3577 		MapBlock *block;
3578 		v3s16 p(x,y,z);
3579 		std::map<v3s16, u8>::iterator n;
3580 		n = m_loaded_blocks.find(p);
3581 		if(n != m_loaded_blocks.end())
3582 			continue;
3583 
3584 		bool block_data_inexistent = false;
3585 		try
3586 		{
3587 			TimeTaker timer1("emerge load");
3588 
3589 			block = m_map->getBlockNoCreate(p);
3590 			if(!block || block->isDummy())
3591 				block_data_inexistent = true;
3592 			else
3593 				block->copyTo(*this);
3594 		}
3595 		catch(InvalidPositionException &e)
3596 		{
3597 			block_data_inexistent = true;
3598 		}
3599 
3600 		if(block_data_inexistent)
3601 		{
3602 
3603 			if (load_if_inexistent) {
3604 				ServerMap *svrmap = (ServerMap *)m_map;
3605 				block = svrmap->emergeBlock(p, false);
3606 				if (block == NULL)
3607 					block = svrmap->createBlock(p);
3608 				else
3609 					block->copyTo(*this);
3610 			} else {
3611 				flags |= VMANIP_BLOCK_DATA_INEXIST;
3612 
3613 				/*
3614 					Mark area inexistent
3615 				*/
3616 				VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3617 				// Fill with VOXELFLAG_NO_DATA
3618 				for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3619 				for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3620 				{
3621 					s32 i = m_area.index(a.MinEdge.X,y,z);
3622 					memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3623 				}
3624 			}
3625 		}
3626 		/*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3627 		{
3628 			// Mark that block was loaded as blank
3629 			flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3630 		}*/
3631 
3632 		m_loaded_blocks[p] = flags;
3633 	}
3634 
3635 	m_is_dirty = false;
3636 }
3637 
blitBackAll(std::map<v3s16,MapBlock * > * modified_blocks,bool overwrite_generated)3638 void ManualMapVoxelManipulator::blitBackAll(
3639 		std::map<v3s16, MapBlock*> *modified_blocks,
3640 		bool overwrite_generated)
3641 {
3642 	if(m_area.getExtent() == v3s16(0,0,0))
3643 		return;
3644 
3645 	/*
3646 		Copy data of all blocks
3647 	*/
3648 	for(std::map<v3s16, u8>::iterator
3649 			i = m_loaded_blocks.begin();
3650 			i != m_loaded_blocks.end(); ++i)
3651 	{
3652 		v3s16 p = i->first;
3653 		MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3654 		bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3655 		if ((existed == false) || (block == NULL) ||
3656 			(overwrite_generated == false && block->isGenerated() == true))
3657 			continue;
3658 
3659 		block->copyFrom(*this);
3660 
3661 		if(modified_blocks)
3662 			(*modified_blocks)[p] = block;
3663 	}
3664 }
3665 
3666 //END
3667