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(¶ms);
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(¶ms);
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