3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
26 #include "serialization.h"
27 #include "nodemetadata.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.h"
35 #include "rollback_interface.h"
36 #include "environment.h"
38 #include "mapgen_v6.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
48 #include "database-leveldb.h"
51 #include "database-redis.h"
54 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
61 Map::Map(std::ostream &dout, IGameDef *gamedef):
65 m_transforming_liquid_loop_count_multiplier(1.0f),
66 m_unprocessed_count(0),
67 m_inc_trending_up_start_time(0),
68 m_queue_size_timer_started(false)
77 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
78 i != m_sectors.end(); ++i)
84 void Map::addEventReceiver(MapEventReceiver *event_receiver)
86 m_event_receivers.insert(event_receiver);
89 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
91 m_event_receivers.erase(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(std::set<MapEventReceiver*>::iterator
97 i = m_event_receivers.begin();
98 i != m_event_receivers.end(); ++i)
100 (*i)->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
113 if(n == m_sectors.end())
116 MapSector *sector = n->second;
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 if (is_valid_position != NULL)
184 *is_valid_position = false;
185 return MapNode(CONTENT_IGNORE);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
190 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
191 if (is_valid_position != NULL)
192 *is_valid_position = is_valid_p;
198 // throws InvalidPositionException if not found
199 // TODO: Now this is deprecated, getNodeNoEx should be renamed
200 MapNode Map::getNode(v3s16 p)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreateNoEx(blockpos);
205 throw InvalidPositionException();
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 bool is_valid_position;
208 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
209 if (!is_valid_position)
210 throw InvalidPositionException();
215 // throws InvalidPositionException if not found
216 void Map::setNode(v3s16 p, MapNode & n)
218 v3s16 blockpos = getNodeBlockPos(p);
219 MapBlock *block = getBlockNoCreate(blockpos);
220 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
221 // Never allow placing CONTENT_IGNORE, it fucks up stuff
222 if(n.getContent() == CONTENT_IGNORE){
224 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
225 <<" while trying to replace \""
226 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
227 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
228 debug_stacks_print_to(infostream);
231 block->setNodeNoCheck(relpos, n);
236 Goes recursively through the neighbours of the node.
238 Alters only transparent nodes.
240 If the lighting of the neighbour is lower than the lighting of
241 the node was (before changing it to 0 at the step before), the
242 lighting of the neighbour is set to 0 and then the same stuff
243 repeats for the neighbour.
245 The ending nodes of the routine are stored in light_sources.
246 This is useful when a light is removed. In such case, this
247 routine can be called for the light node and then again for
248 light_sources to re-light the area without the removed light.
250 values of from_nodes are lighting values.
252 void Map::unspreadLight(enum LightBank bank,
253 std::map<v3s16, u8> & from_nodes,
254 std::set<v3s16> & light_sources,
255 std::map<v3s16, MapBlock*> & modified_blocks)
257 INodeDefManager *nodemgr = m_gamedef->ndef();
260 v3s16(0,0,1), // back
262 v3s16(1,0,0), // right
263 v3s16(0,0,-1), // front
264 v3s16(0,-1,0), // bottom
265 v3s16(-1,0,0), // left
268 if(from_nodes.empty())
271 u32 blockchangecount = 0;
273 std::map<v3s16, u8> unlighted_nodes;
276 Initialize block cache
279 MapBlock *block = NULL;
280 // Cache this a bit, too
281 bool block_checked_in_modified = false;
283 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
284 j != from_nodes.end(); ++j)
286 v3s16 pos = j->first;
287 v3s16 blockpos = getNodeBlockPos(pos);
289 // Only fetch a new block if the block position has changed
291 if(block == NULL || blockpos != blockpos_last){
292 block = getBlockNoCreate(blockpos);
293 blockpos_last = blockpos;
295 block_checked_in_modified = false;
299 catch(InvalidPositionException &e)
307 // Calculate relative position in block
308 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 //MapNode n = block->getNode(relpos);
313 u8 oldlight = j->second;
315 // Loop through 6 neighbors
316 for(u16 i=0; i<6; i++)
318 // Get the position of the neighbor node
319 v3s16 n2pos = pos + dirs[i];
321 // Get the block where the node is located
322 v3s16 blockpos, relpos;
323 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
325 // Only fetch a new block if the block position has changed
327 if(block == NULL || blockpos != blockpos_last){
328 block = getBlockNoCreate(blockpos);
329 blockpos_last = blockpos;
331 block_checked_in_modified = false;
335 catch(InvalidPositionException &e) {
339 // Get node straight from the block
340 bool is_valid_position;
341 MapNode n2 = block->getNode(relpos, &is_valid_position);
342 if (!is_valid_position)
345 bool changed = false;
347 //TODO: Optimize output by optimizing light_sources?
350 If the neighbor is dimmer than what was specified
351 as oldlight (the light of the previous node)
353 if(n2.getLight(bank, nodemgr) < oldlight)
356 And the neighbor is transparent and it has some light
358 if(nodemgr->get(n2).light_propagates
359 && n2.getLight(bank, nodemgr) != 0)
362 Set light to 0 and add to queue
365 u8 current_light = n2.getLight(bank, nodemgr);
366 n2.setLight(bank, 0, nodemgr);
367 block->setNode(relpos, n2);
369 unlighted_nodes[n2pos] = current_light;
373 Remove from light_sources if it is there
374 NOTE: This doesn't happen nearly at all
376 /*if(light_sources.find(n2pos))
378 infostream<<"Removed from light_sources"<<std::endl;
379 light_sources.remove(n2pos);
384 if(light_sources.find(n2pos) != NULL)
385 light_sources.remove(n2pos);*/
388 light_sources.insert(n2pos);
391 // Add to modified_blocks
392 if(changed == true && block_checked_in_modified == false)
394 // If the block is not found in modified_blocks, add.
395 if(modified_blocks.find(blockpos) == modified_blocks.end())
397 modified_blocks[blockpos] = block;
399 block_checked_in_modified = true;
404 /*infostream<<"unspreadLight(): Changed block "
405 <<blockchangecount<<" times"
406 <<" for "<<from_nodes.size()<<" nodes"
409 if(!unlighted_nodes.empty())
410 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
414 A single-node wrapper of the above
416 void Map::unLightNeighbors(enum LightBank bank,
417 v3s16 pos, u8 lightwas,
418 std::set<v3s16> & light_sources,
419 std::map<v3s16, MapBlock*> & modified_blocks)
421 std::map<v3s16, u8> from_nodes;
422 from_nodes[pos] = lightwas;
424 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
428 Lights neighbors of from_nodes, collects all them and then
431 void Map::spreadLight(enum LightBank bank,
432 std::set<v3s16> & from_nodes,
433 std::map<v3s16, MapBlock*> & modified_blocks)
435 INodeDefManager *nodemgr = m_gamedef->ndef();
437 const v3s16 dirs[6] = {
438 v3s16(0,0,1), // back
440 v3s16(1,0,0), // right
441 v3s16(0,0,-1), // front
442 v3s16(0,-1,0), // bottom
443 v3s16(-1,0,0), // left
446 if(from_nodes.empty())
449 u32 blockchangecount = 0;
451 std::set<v3s16> lighted_nodes;
454 Initialize block cache
457 MapBlock *block = NULL;
458 // Cache this a bit, too
459 bool block_checked_in_modified = false;
461 for(std::set<v3s16>::iterator j = from_nodes.begin();
462 j != from_nodes.end(); ++j)
465 v3s16 blockpos, relpos;
467 getNodeBlockPosWithOffset(pos, blockpos, relpos);
469 // Only fetch a new block if the block position has changed
471 if(block == NULL || blockpos != blockpos_last){
472 block = getBlockNoCreate(blockpos);
473 blockpos_last = blockpos;
475 block_checked_in_modified = false;
479 catch(InvalidPositionException &e) {
486 // Get node straight from the block
487 bool is_valid_position;
488 MapNode n = block->getNode(relpos, &is_valid_position);
490 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
491 u8 newlight = diminish_light(oldlight);
493 // Loop through 6 neighbors
494 for(u16 i=0; i<6; i++){
495 // Get the position of the neighbor node
496 v3s16 n2pos = pos + dirs[i];
498 // Get the block where the node is located
499 v3s16 blockpos, relpos;
500 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
502 // Only fetch a new block if the block position has changed
504 if(block == NULL || blockpos != blockpos_last){
505 block = getBlockNoCreate(blockpos);
506 blockpos_last = blockpos;
508 block_checked_in_modified = false;
512 catch(InvalidPositionException &e) {
516 // Get node straight from the block
517 MapNode n2 = block->getNode(relpos, &is_valid_position);
518 if (!is_valid_position)
521 bool changed = false;
523 If the neighbor is brighter than the current node,
524 add to list (it will light up this node on its turn)
526 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
528 lighted_nodes.insert(n2pos);
532 If the neighbor is dimmer than how much light this node
533 would spread on it, add to list
535 if(n2.getLight(bank, nodemgr) < newlight)
537 if(nodemgr->get(n2).light_propagates)
539 n2.setLight(bank, newlight, nodemgr);
540 block->setNode(relpos, n2);
541 lighted_nodes.insert(n2pos);
546 // Add to modified_blocks
547 if(changed == true && block_checked_in_modified == false)
549 // If the block is not found in modified_blocks, add.
550 if(modified_blocks.find(blockpos) == modified_blocks.end())
552 modified_blocks[blockpos] = block;
554 block_checked_in_modified = true;
559 /*infostream<<"spreadLight(): Changed block "
560 <<blockchangecount<<" times"
561 <<" for "<<from_nodes.size()<<" nodes"
564 if(!lighted_nodes.empty())
565 spreadLight(bank, lighted_nodes, modified_blocks);
569 A single-node source variation of the above.
571 void Map::lightNeighbors(enum LightBank bank,
573 std::map<v3s16, MapBlock*> & modified_blocks)
575 std::set<v3s16> from_nodes;
576 from_nodes.insert(pos);
577 spreadLight(bank, from_nodes, modified_blocks);
580 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
582 INodeDefManager *nodemgr = m_gamedef->ndef();
585 v3s16(0,0,1), // back
587 v3s16(1,0,0), // right
588 v3s16(0,0,-1), // front
589 v3s16(0,-1,0), // bottom
590 v3s16(-1,0,0), // left
593 u8 brightest_light = 0;
594 v3s16 brightest_pos(0,0,0);
595 bool found_something = false;
597 // Loop through 6 neighbors
598 for(u16 i=0; i<6; i++){
599 // Get the position of the neighbor node
600 v3s16 n2pos = p + dirs[i];
602 bool is_valid_position;
603 n2 = getNodeNoEx(n2pos, &is_valid_position);
604 if (!is_valid_position)
607 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
608 brightest_light = n2.getLight(bank, nodemgr);
609 brightest_pos = n2pos;
610 found_something = true;
614 if(found_something == false)
615 throw InvalidPositionException();
617 return brightest_pos;
621 Propagates sunlight down from a node.
622 Starting point gets sunlight.
624 Returns the lowest y value of where the sunlight went.
626 Mud is turned into grass in where the sunlight stops.
628 s16 Map::propagateSunlight(v3s16 start,
629 std::map<v3s16, MapBlock*> & modified_blocks)
631 INodeDefManager *nodemgr = m_gamedef->ndef();
636 v3s16 pos(start.X, y, start.Z);
638 v3s16 blockpos = getNodeBlockPos(pos);
641 block = getBlockNoCreate(blockpos);
643 catch(InvalidPositionException &e)
648 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
649 bool is_valid_position;
650 MapNode n = block->getNode(relpos, &is_valid_position);
651 if (!is_valid_position)
654 if(nodemgr->get(n).sunlight_propagates)
656 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
657 block->setNode(relpos, n);
659 modified_blocks[blockpos] = block;
663 // Sunlight goes no further
670 void Map::updateLighting(enum LightBank bank,
671 std::map<v3s16, MapBlock*> & a_blocks,
672 std::map<v3s16, MapBlock*> & modified_blocks)
674 INodeDefManager *nodemgr = m_gamedef->ndef();
676 /*m_dout<<DTIME<<"Map::updateLighting(): "
677 <<a_blocks.size()<<" blocks."<<std::endl;*/
679 //TimeTaker timer("updateLighting");
683 //u32 count_was = modified_blocks.size();
685 //std::map<v3s16, MapBlock*> blocks_to_update;
687 std::set<v3s16> light_sources;
689 std::map<v3s16, u8> unlight_from;
691 int num_bottom_invalid = 0;
694 //TimeTaker t("first stuff");
696 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
697 i != a_blocks.end(); ++i)
699 MapBlock *block = i->second;
703 // Don't bother with dummy blocks.
707 v3s16 pos = block->getPos();
708 v3s16 posnodes = block->getPosRelative();
709 modified_blocks[pos] = block;
710 //blocks_to_update[pos] = block;
713 Clear all light from block
715 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
716 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
717 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
720 bool is_valid_position;
721 MapNode n = block->getNode(p, &is_valid_position);
722 if (!is_valid_position) {
723 /* This would happen when dealing with a
726 infostream<<"updateLighting(): InvalidPositionException"
730 u8 oldlight = n.getLight(bank, nodemgr);
731 n.setLight(bank, 0, nodemgr);
732 block->setNode(p, n);
734 // If node sources light, add to list
735 u8 source = nodemgr->get(n).light_source;
737 light_sources.insert(p + posnodes);
739 // Collect borders for unlighting
740 if((x==0 || x == MAP_BLOCKSIZE-1
741 || y==0 || y == MAP_BLOCKSIZE-1
742 || z==0 || z == MAP_BLOCKSIZE-1)
745 v3s16 p_map = p + posnodes;
746 unlight_from[p_map] = oldlight;
752 if(bank == LIGHTBANK_DAY)
754 bool bottom_valid = block->propagateSunlight(light_sources);
757 num_bottom_invalid++;
759 // If bottom is valid, we're done.
763 else if(bank == LIGHTBANK_NIGHT)
765 // For night lighting, sunlight is not propagated
770 assert("Invalid lighting bank" == NULL);
773 /*infostream<<"Bottom for sunlight-propagated block ("
774 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
777 // Bottom sunlight is not valid; get the block and loop to it
781 block = getBlockNoCreate(pos);
783 catch(InvalidPositionException &e)
785 FATAL_ERROR("Invalid position");
794 Enable this to disable proper lighting for speeding up map
795 generation for testing or whatever
798 //if(g_settings->get(""))
800 core::map<v3s16, MapBlock*>::Iterator i;
801 i = blocks_to_update.getIterator();
802 for(; i.atEnd() == false; i++)
804 MapBlock *block = i.getNode()->getValue();
805 v3s16 p = block->getPos();
806 block->setLightingExpired(false);
814 //TimeTaker timer("unspreadLight");
815 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
820 u32 diff = modified_blocks.size() - count_was;
821 count_was = modified_blocks.size();
822 infostream<<"unspreadLight modified "<<diff<<std::endl;
826 //TimeTaker timer("spreadLight");
827 spreadLight(bank, light_sources, modified_blocks);
832 u32 diff = modified_blocks.size() - count_was;
833 count_was = modified_blocks.size();
834 infostream<<"spreadLight modified "<<diff<<std::endl;
840 //MapVoxelManipulator vmanip(this);
842 // Make a manual voxel manipulator and load all the blocks
843 // that touch the requested blocks
844 ManualMapVoxelManipulator vmanip(this);
847 //TimeTaker timer("initialEmerge");
849 core::map<v3s16, MapBlock*>::Iterator i;
850 i = blocks_to_update.getIterator();
851 for(; i.atEnd() == false; i++)
853 MapBlock *block = i.getNode()->getValue();
854 v3s16 p = block->getPos();
856 // Add all surrounding blocks
857 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
860 Add all surrounding blocks that have up-to-date lighting
861 NOTE: This doesn't quite do the job (not everything
862 appropriate is lighted)
864 /*for(s16 z=-1; z<=1; z++)
865 for(s16 y=-1; y<=1; y++)
866 for(s16 x=-1; x<=1; x++)
868 v3s16 p2 = p + v3s16(x,y,z);
869 MapBlock *block = getBlockNoCreateNoEx(p2);
874 if(block->getLightingExpired())
876 vmanip.initialEmerge(p2, p2);
879 // Lighting of block will be updated completely
880 block->setLightingExpired(false);
885 //TimeTaker timer("unSpreadLight");
886 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
889 //TimeTaker timer("spreadLight");
890 vmanip.spreadLight(bank, light_sources, nodemgr);
893 //TimeTaker timer("blitBack");
894 vmanip.blitBack(modified_blocks);
896 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
901 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
904 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
905 std::map<v3s16, MapBlock*> & modified_blocks)
907 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
908 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
911 Update information about whether day and night light differ
913 for(std::map<v3s16, MapBlock*>::iterator
914 i = modified_blocks.begin();
915 i != modified_blocks.end(); ++i)
917 MapBlock *block = i->second;
918 block->expireDayNightDiff();
924 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
925 std::map<v3s16, MapBlock*> &modified_blocks,
926 bool remove_metadata)
928 INodeDefManager *ndef = m_gamedef->ndef();
931 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
932 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
935 From this node to nodes underneath:
936 If lighting is sunlight (1.0), unlight neighbours and
941 v3s16 toppos = p + v3s16(0,1,0);
942 //v3s16 bottompos = p + v3s16(0,-1,0);
944 bool node_under_sunlight = true;
945 std::set<v3s16> light_sources;
948 Collect old node for rollback
950 RollbackNode rollback_oldnode(this, p, m_gamedef);
953 If there is a node at top and it doesn't have sunlight,
954 there has not been any sunlight going down.
956 Otherwise there probably is.
959 bool is_valid_position;
960 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
962 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
963 node_under_sunlight = false;
966 Remove all light that has come out of this node
969 enum LightBank banks[] =
974 for(s32 i=0; i<2; i++)
976 enum LightBank bank = banks[i];
978 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
980 // Add the block of the added node to modified_blocks
981 v3s16 blockpos = getNodeBlockPos(p);
982 MapBlock * block = getBlockNoCreate(blockpos);
983 assert(block != NULL);
984 modified_blocks[blockpos] = block;
986 assert(isValidPosition(p));
988 // Unlight neighbours of node.
989 // This means setting light of all consequent dimmer nodes
991 // This also collects the nodes at the border which will spread
992 // light again into this.
993 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
995 n.setLight(bank, 0, ndef);
999 If node lets sunlight through and is under sunlight, it has
1002 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1004 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1008 Remove node metadata
1010 if (remove_metadata) {
1011 removeNodeMetadata(p);
1015 Set the node on the map
1021 If node is under sunlight and doesn't let sunlight through,
1022 take all sunlighted nodes under it and clear light from them
1023 and from where the light has been spread.
1024 TODO: This could be optimized by mass-unlighting instead
1027 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1031 //m_dout<<DTIME<<"y="<<y<<std::endl;
1032 v3s16 n2pos(p.X, y, p.Z);
1036 n2 = getNodeNoEx(n2pos, &is_valid_position);
1037 if (!is_valid_position)
1040 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1042 unLightNeighbors(LIGHTBANK_DAY,
1043 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1044 light_sources, modified_blocks);
1045 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1053 for(s32 i=0; i<2; i++)
1055 enum LightBank bank = banks[i];
1058 Spread light from all nodes that might be capable of doing so
1060 spreadLight(bank, light_sources, modified_blocks);
1064 Update information about whether day and night light differ
1066 for(std::map<v3s16, MapBlock*>::iterator
1067 i = modified_blocks.begin();
1068 i != modified_blocks.end(); ++i)
1070 i->second->expireDayNightDiff();
1076 if(m_gamedef->rollback())
1078 RollbackNode rollback_newnode(this, p, m_gamedef);
1079 RollbackAction action;
1080 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1081 m_gamedef->rollback()->reportAction(action);
1085 Add neighboring liquid nodes and the node itself if it is
1086 liquid (=water node was added) to transform queue.
1089 v3s16(0,0,0), // self
1090 v3s16(0,0,1), // back
1091 v3s16(0,1,0), // top
1092 v3s16(1,0,0), // right
1093 v3s16(0,0,-1), // front
1094 v3s16(0,-1,0), // bottom
1095 v3s16(-1,0,0), // left
1097 for(u16 i=0; i<7; i++)
1099 v3s16 p2 = p + dirs[i];
1101 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1102 if(is_valid_position
1103 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1105 m_transforming_liquid.push_back(p2);
1112 void Map::removeNodeAndUpdate(v3s16 p,
1113 std::map<v3s16, MapBlock*> &modified_blocks)
1115 INodeDefManager *ndef = m_gamedef->ndef();
1117 /*PrintInfo(m_dout);
1118 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1119 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1121 bool node_under_sunlight = true;
1123 v3s16 toppos = p + v3s16(0,1,0);
1125 // Node will be replaced with this
1126 content_t replace_material = CONTENT_AIR;
1129 Collect old node for rollback
1131 RollbackNode rollback_oldnode(this, p, m_gamedef);
1134 If there is a node at top and it doesn't have sunlight,
1135 there will be no sunlight going down.
1137 bool is_valid_position;
1138 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1140 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1141 node_under_sunlight = false;
1143 std::set<v3s16> light_sources;
1145 enum LightBank banks[] =
1150 for(s32 i=0; i<2; i++)
1152 enum LightBank bank = banks[i];
1155 Unlight neighbors (in case the node is a light source)
1157 unLightNeighbors(bank, p,
1158 getNodeNoEx(p).getLight(bank, ndef),
1159 light_sources, modified_blocks);
1163 Remove node metadata
1166 removeNodeMetadata(p);
1170 This also clears the lighting.
1173 MapNode n(replace_material);
1176 for(s32 i=0; i<2; i++)
1178 enum LightBank bank = banks[i];
1181 Recalculate lighting
1183 spreadLight(bank, light_sources, modified_blocks);
1186 // Add the block of the removed node to modified_blocks
1187 v3s16 blockpos = getNodeBlockPos(p);
1188 MapBlock * block = getBlockNoCreate(blockpos);
1189 assert(block != NULL);
1190 modified_blocks[blockpos] = block;
1193 If the removed node was under sunlight, propagate the
1194 sunlight down from it and then light all neighbors
1195 of the propagated blocks.
1197 if(node_under_sunlight)
1199 s16 ybottom = propagateSunlight(p, modified_blocks);
1200 /*m_dout<<DTIME<<"Node was under sunlight. "
1201 "Propagating sunlight";
1202 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1204 for(; y >= ybottom; y--)
1206 v3s16 p2(p.X, y, p.Z);
1207 /*m_dout<<DTIME<<"lighting neighbors of node ("
1208 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1210 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1215 // Set the lighting of this node to 0
1216 // TODO: Is this needed? Lighting is cleared up there already.
1217 MapNode n = getNodeNoEx(p, &is_valid_position);
1218 if (is_valid_position) {
1219 n.setLight(LIGHTBANK_DAY, 0, ndef);
1222 FATAL_ERROR("Invalid position");
1226 for(s32 i=0; i<2; i++)
1228 enum LightBank bank = banks[i];
1230 // Get the brightest neighbour node and propagate light from it
1231 v3s16 n2p = getBrightestNeighbour(bank, p);
1233 //MapNode n2 = getNode(n2p);
1234 lightNeighbors(bank, n2p, modified_blocks);
1236 catch(InvalidPositionException &e)
1242 Update information about whether day and night light differ
1244 for(std::map<v3s16, MapBlock*>::iterator
1245 i = modified_blocks.begin();
1246 i != modified_blocks.end(); ++i)
1248 i->second->expireDayNightDiff();
1254 if(m_gamedef->rollback())
1256 RollbackNode rollback_newnode(this, p, m_gamedef);
1257 RollbackAction action;
1258 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1259 m_gamedef->rollback()->reportAction(action);
1263 Add neighboring liquid nodes and this node to transform queue.
1264 (it's vital for the node itself to get updated last.)
1267 v3s16(0,0,1), // back
1268 v3s16(0,1,0), // top
1269 v3s16(1,0,0), // right
1270 v3s16(0,0,-1), // front
1271 v3s16(0,-1,0), // bottom
1272 v3s16(-1,0,0), // left
1273 v3s16(0,0,0), // self
1275 for(u16 i=0; i<7; i++)
1277 v3s16 p2 = p + dirs[i];
1279 bool is_position_valid;
1280 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1281 if (is_position_valid
1282 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1284 m_transforming_liquid.push_back(p2);
1289 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1292 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1296 bool succeeded = true;
1298 std::map<v3s16, MapBlock*> modified_blocks;
1299 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1301 // Copy modified_blocks to event
1302 for(std::map<v3s16, MapBlock*>::iterator
1303 i = modified_blocks.begin();
1304 i != modified_blocks.end(); ++i)
1306 event.modified_blocks.insert(i->first);
1309 catch(InvalidPositionException &e){
1313 dispatchEvent(&event);
1318 bool Map::removeNodeWithEvent(v3s16 p)
1321 event.type = MEET_REMOVENODE;
1324 bool succeeded = true;
1326 std::map<v3s16, MapBlock*> modified_blocks;
1327 removeNodeAndUpdate(p, modified_blocks);
1329 // Copy modified_blocks to event
1330 for(std::map<v3s16, MapBlock*>::iterator
1331 i = modified_blocks.begin();
1332 i != modified_blocks.end(); ++i)
1334 event.modified_blocks.insert(i->first);
1337 catch(InvalidPositionException &e){
1341 dispatchEvent(&event);
1346 bool Map::getDayNightDiff(v3s16 blockpos)
1349 v3s16 p = blockpos + v3s16(0,0,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->getDayNightDiff())
1354 catch(InvalidPositionException &e){}
1357 v3s16 p = blockpos + v3s16(-1,0,0);
1358 MapBlock *b = getBlockNoCreate(p);
1359 if(b->getDayNightDiff())
1362 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(0,-1,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->getDayNightDiff())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,0,-1);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->getDayNightDiff())
1376 catch(InvalidPositionException &e){}
1379 v3s16 p = blockpos + v3s16(1,0,0);
1380 MapBlock *b = getBlockNoCreate(p);
1381 if(b->getDayNightDiff())
1384 catch(InvalidPositionException &e){}
1386 v3s16 p = blockpos + v3s16(0,1,0);
1387 MapBlock *b = getBlockNoCreate(p);
1388 if(b->getDayNightDiff())
1391 catch(InvalidPositionException &e){}
1393 v3s16 p = blockpos + v3s16(0,0,1);
1394 MapBlock *b = getBlockNoCreate(p);
1395 if(b->getDayNightDiff())
1398 catch(InvalidPositionException &e){}
1403 struct TimeOrderedMapBlock {
1407 TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
1412 bool operator<(const TimeOrderedMapBlock &b) const
1414 return block->getUsageTimer() < b.block->getUsageTimer();
1419 Updates usage timers
1421 void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
1422 std::vector<v3s16> *unloaded_blocks)
1424 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1426 // Profile modified reasons
1427 Profiler modprofiler;
1429 std::vector<v2s16> sector_deletion_queue;
1430 u32 deleted_blocks_count = 0;
1431 u32 saved_blocks_count = 0;
1432 u32 block_count_all = 0;
1436 // If there is no practical limit, we spare creation of mapblock_queue
1437 if (max_loaded_blocks == (u32)-1) {
1438 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1439 si != m_sectors.end(); ++si) {
1440 MapSector *sector = si->second;
1442 bool all_blocks_deleted = true;
1444 MapBlockVect blocks;
1445 sector->getBlocks(blocks);
1447 for (MapBlockVect::iterator i = blocks.begin();
1448 i != blocks.end(); ++i) {
1449 MapBlock *block = (*i);
1451 block->incrementUsageTimer(dtime);
1453 if (block->refGet() == 0
1454 && block->getUsageTimer() > unload_timeout) {
1455 v3s16 p = block->getPos();
1458 if (block->getModified() != MOD_STATE_CLEAN
1459 && save_before_unloading) {
1460 modprofiler.add(block->getModifiedReasonString(), 1);
1461 if (!saveBlock(block))
1463 saved_blocks_count++;
1466 // Delete from memory
1467 sector->deleteBlock(block);
1469 if (unloaded_blocks)
1470 unloaded_blocks->push_back(p);
1472 deleted_blocks_count++;
1474 all_blocks_deleted = false;
1479 if (all_blocks_deleted) {
1480 sector_deletion_queue.push_back(si->first);
1484 std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
1485 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1486 si != m_sectors.end(); ++si) {
1487 MapSector *sector = si->second;
1489 MapBlockVect blocks;
1490 sector->getBlocks(blocks);
1492 for(MapBlockVect::iterator i = blocks.begin();
1493 i != blocks.end(); ++i) {
1494 MapBlock *block = (*i);
1496 block->incrementUsageTimer(dtime);
1497 mapblock_queue.push(TimeOrderedMapBlock(sector, block));
1500 block_count_all = mapblock_queue.size();
1501 // Delete old blocks, and blocks over the limit from the memory
1502 while (mapblock_queue.size() > max_loaded_blocks
1503 || mapblock_queue.top().block->getUsageTimer() > unload_timeout) {
1504 TimeOrderedMapBlock b = mapblock_queue.top();
1505 mapblock_queue.pop();
1507 MapBlock *block = b.block;
1509 if (block->refGet() != 0)
1512 v3s16 p = block->getPos();
1515 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1516 modprofiler.add(block->getModifiedReasonString(), 1);
1517 if (!saveBlock(block))
1519 saved_blocks_count++;
1522 // Delete from memory
1523 b.sect->deleteBlock(block);
1525 if (unloaded_blocks)
1526 unloaded_blocks->push_back(p);
1528 deleted_blocks_count++;
1531 // Delete empty sectors
1532 for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1533 si != m_sectors.end(); ++si) {
1534 if (si->second->empty()) {
1535 sector_deletion_queue.push_back(si->first);
1541 // Finally delete the empty sectors
1542 deleteSectors(sector_deletion_queue);
1544 if(deleted_blocks_count != 0)
1546 PrintInfo(infostream); // ServerMap/ClientMap:
1547 infostream<<"Unloaded "<<deleted_blocks_count
1548 <<" blocks from memory";
1549 if(save_before_unloading)
1550 infostream<<", of which "<<saved_blocks_count<<" were written";
1551 infostream<<", "<<block_count_all<<" blocks in memory";
1552 infostream<<"."<<std::endl;
1553 if(saved_blocks_count != 0){
1554 PrintInfo(infostream); // ServerMap/ClientMap:
1555 infostream<<"Blocks modified by: "<<std::endl;
1556 modprofiler.print(infostream);
1561 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1563 timerUpdate(0.0, -1.0, 0, unloaded_blocks);
1566 void Map::deleteSectors(std::vector<v2s16> §orList)
1568 for(std::vector<v2s16>::iterator j = sectorList.begin();
1569 j != sectorList.end(); ++j) {
1570 MapSector *sector = m_sectors[*j];
1571 // If sector is in sector cache, remove it from there
1572 if(m_sector_cache == sector)
1573 m_sector_cache = NULL;
1574 // Remove from map and delete
1575 m_sectors.erase(*j);
1580 void Map::PrintInfo(std::ostream &out)
1585 #define WATER_DROP_BOOST 4
1589 NEIGHBOR_SAME_LEVEL,
1592 struct NodeNeighbor {
1596 bool l; //can liquid
1602 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1609 void Map::transforming_liquid_add(v3s16 p) {
1610 m_transforming_liquid.push_back(p);
1613 s32 Map::transforming_liquid_size() {
1614 return m_transforming_liquid.size();
1617 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1620 INodeDefManager *nodemgr = m_gamedef->ndef();
1622 DSTACK(__FUNCTION_NAME);
1623 //TimeTaker timer("transformLiquids()");
1626 u32 initial_size = m_transforming_liquid.size();
1628 /*if(initial_size != 0)
1629 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1631 // list of nodes that due to viscosity have not reached their max level height
1632 std::deque<v3s16> must_reflow;
1634 // List of MapBlocks that will require a lighting update (due to lava)
1635 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1637 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1638 u32 loop_max = liquid_loop_max;
1642 /* If liquid_loop_max is not keeping up with the queue size increase
1643 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1645 if (m_transforming_liquid.size() > loop_max * 2) {
1647 float server_step = g_settings->getFloat("dedicated_server_step");
1648 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1649 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1651 m_transforming_liquid_loop_count_multiplier = 1.0;
1654 loop_max *= m_transforming_liquid_loop_count_multiplier;
1657 while(m_transforming_liquid.size() != 0)
1659 // This should be done here so that it is done when continue is used
1660 if(loopcount >= initial_size || loopcount >= loop_max)
1665 Get a queued transforming liquid node
1667 v3s16 p0 = m_transforming_liquid.front();
1668 m_transforming_liquid.pop_front();
1670 MapNode n0 = getNodeNoEx(p0);
1673 Collect information about current node
1675 s8 liquid_level = -1;
1676 content_t liquid_kind = CONTENT_IGNORE;
1677 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1678 switch (liquid_type) {
1680 liquid_level = LIQUID_LEVEL_SOURCE;
1681 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1683 case LIQUID_FLOWING:
1684 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1685 liquid_kind = n0.getContent();
1688 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1689 // continue with the next node.
1690 if (n0.getContent() != CONTENT_AIR)
1692 liquid_kind = CONTENT_AIR;
1697 Collect information about the environment
1699 const v3s16 *dirs = g_6dirs;
1700 NodeNeighbor sources[6]; // surrounding sources
1701 int num_sources = 0;
1702 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1704 NodeNeighbor airs[6]; // surrounding air
1706 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1707 int num_neutrals = 0;
1708 bool flowing_down = false;
1709 for (u16 i = 0; i < 6; i++) {
1710 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1713 nt = NEIGHBOR_UPPER;
1716 nt = NEIGHBOR_LOWER;
1719 v3s16 npos = p0 + dirs[i];
1720 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1721 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1723 if (nb.n.getContent() == CONTENT_AIR) {
1724 airs[num_airs++] = nb;
1725 // if the current node is a water source the neighbor
1726 // should be enqueded for transformation regardless of whether the
1727 // current node changes or not.
1728 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1729 m_transforming_liquid.push_back(npos);
1730 // if the current node happens to be a flowing node, it will start to flow down here.
1731 if (nb.t == NEIGHBOR_LOWER) {
1732 flowing_down = true;
1735 neutrals[num_neutrals++] = nb;
1739 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1740 if (liquid_kind == CONTENT_AIR)
1741 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1742 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1743 neutrals[num_neutrals++] = nb;
1745 // Do not count bottom source, it will screw things up
1747 sources[num_sources++] = nb;
1750 case LIQUID_FLOWING:
1751 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1752 if (liquid_kind == CONTENT_AIR)
1753 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1754 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1755 neutrals[num_neutrals++] = nb;
1757 flows[num_flows++] = nb;
1758 if (nb.t == NEIGHBOR_LOWER)
1759 flowing_down = true;
1766 decide on the type (and possibly level) of the current node
1768 content_t new_node_content;
1769 s8 new_node_level = -1;
1770 s8 max_node_level = -1;
1772 u8 range = nodemgr->get(liquid_kind).liquid_range;
1773 if (range > LIQUID_LEVEL_MAX+1)
1774 range = LIQUID_LEVEL_MAX+1;
1776 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1777 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1778 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1779 // it's perfectly safe to use liquid_kind here to determine the new node content.
1780 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1781 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1782 // liquid_kind is set properly, see above
1783 new_node_content = liquid_kind;
1784 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1785 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1786 new_node_content = CONTENT_AIR;
1788 // no surrounding sources, so get the maximum level that can flow into this node
1789 for (u16 i = 0; i < num_flows; i++) {
1790 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1791 switch (flows[i].t) {
1792 case NEIGHBOR_UPPER:
1793 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1794 max_node_level = LIQUID_LEVEL_MAX;
1795 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1796 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1797 } else if (nb_liquid_level > max_node_level)
1798 max_node_level = nb_liquid_level;
1800 case NEIGHBOR_LOWER:
1802 case NEIGHBOR_SAME_LEVEL:
1803 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1804 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1805 max_node_level = nb_liquid_level - 1;
1811 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1812 if (viscosity > 1 && max_node_level != liquid_level) {
1813 // amount to gain, limited by viscosity
1814 // must be at least 1 in absolute value
1815 s8 level_inc = max_node_level - liquid_level;
1816 if (level_inc < -viscosity || level_inc > viscosity)
1817 new_node_level = liquid_level + level_inc/viscosity;
1818 else if (level_inc < 0)
1819 new_node_level = liquid_level - 1;
1820 else if (level_inc > 0)
1821 new_node_level = liquid_level + 1;
1822 if (new_node_level != max_node_level)
1823 must_reflow.push_back(p0);
1825 new_node_level = max_node_level;
1827 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1828 new_node_content = liquid_kind;
1830 new_node_content = CONTENT_AIR;
1835 check if anything has changed. if not, just continue with the next node.
1837 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1838 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1839 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1845 update the current node
1848 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1849 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1850 // set level to last 3 bits, flowing down bit to 4th bit
1851 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1853 // set the liquid level and flow bit to 0
1854 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1856 n0.setContent(new_node_content);
1858 // Find out whether there is a suspect for this action
1859 std::string suspect;
1860 if(m_gamedef->rollback()) {
1861 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1864 if(m_gamedef->rollback() && !suspect.empty()){
1866 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1867 // Get old node for rollback
1868 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1872 RollbackNode rollback_newnode(this, p0, m_gamedef);
1873 RollbackAction action;
1874 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1875 m_gamedef->rollback()->reportAction(action);
1881 v3s16 blockpos = getNodeBlockPos(p0);
1882 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1884 modified_blocks[blockpos] = block;
1885 // If new or old node emits light, MapBlock requires lighting update
1886 if(nodemgr->get(n0).light_source != 0 ||
1887 nodemgr->get(n00).light_source != 0)
1888 lighting_modified_blocks[block->getPos()] = block;
1892 enqueue neighbors for update if neccessary
1894 switch (nodemgr->get(n0.getContent()).liquid_type) {
1896 case LIQUID_FLOWING:
1897 // make sure source flows into all neighboring nodes
1898 for (u16 i = 0; i < num_flows; i++)
1899 if (flows[i].t != NEIGHBOR_UPPER)
1900 m_transforming_liquid.push_back(flows[i].p);
1901 for (u16 i = 0; i < num_airs; i++)
1902 if (airs[i].t != NEIGHBOR_UPPER)
1903 m_transforming_liquid.push_back(airs[i].p);
1906 // this flow has turned to air; neighboring flows might need to do the same
1907 for (u16 i = 0; i < num_flows; i++)
1908 m_transforming_liquid.push_back(flows[i].p);
1912 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1914 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1915 m_transforming_liquid.push_back(*iter);
1917 updateLighting(lighting_modified_blocks, modified_blocks);
1920 /* ----------------------------------------------------------------------
1921 * Manage the queue so that it does not grow indefinately
1923 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1925 if (time_until_purge == 0)
1926 return; // Feature disabled
1928 time_until_purge *= 1000; // seconds -> milliseconds
1930 u32 curr_time = getTime(PRECISION_MILLI);
1931 u32 prev_unprocessed = m_unprocessed_count;
1932 m_unprocessed_count = m_transforming_liquid.size();
1934 // if unprocessed block count is decreasing or stable
1935 if (m_unprocessed_count <= prev_unprocessed) {
1936 m_queue_size_timer_started = false;
1938 if (!m_queue_size_timer_started)
1939 m_inc_trending_up_start_time = curr_time;
1940 m_queue_size_timer_started = true;
1943 // Account for curr_time overflowing
1944 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1945 m_queue_size_timer_started = false;
1947 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1948 * and the number of unprocessed blocks is still > liquid_loop_max then we
1949 * cannot keep up; dump the oldest blocks from the queue so that the queue
1950 * has liquid_loop_max items in it
1952 if (m_queue_size_timer_started
1953 && curr_time - m_inc_trending_up_start_time > time_until_purge
1954 && m_unprocessed_count > liquid_loop_max) {
1956 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1958 infostream << "transformLiquids(): DUMPING " << dump_qty
1959 << " blocks from the queue" << std::endl;
1962 m_transforming_liquid.pop_front();
1964 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1965 m_unprocessed_count = m_transforming_liquid.size();
1969 std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
1971 std::vector<v3s16> positions_with_meta;
1973 sortBoxVerticies(p1, p2);
1974 v3s16 bpmin = getNodeBlockPos(p1);
1975 v3s16 bpmax = getNodeBlockPos(p2);
1977 VoxelArea area(p1, p2);
1979 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1980 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1981 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1982 v3s16 blockpos(x, y, z);
1984 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1986 verbosestream << "Map::getNodeMetadata(): Need to emerge "
1987 << PP(blockpos) << std::endl;
1988 block = emergeBlock(blockpos, false);
1991 infostream << "WARNING: Map::getNodeMetadata(): Block not found"
1996 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
1997 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
1998 for (size_t i = 0; i != keys.size(); i++) {
1999 v3s16 p(keys[i] + p_base);
2000 if (!area.contains(p))
2003 positions_with_meta.push_back(p);
2007 return positions_with_meta;
2010 NodeMetadata *Map::getNodeMetadata(v3s16 p)
2012 v3s16 blockpos = getNodeBlockPos(p);
2013 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2014 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2016 infostream<<"Map::getNodeMetadata(): Need to emerge "
2017 <<PP(blockpos)<<std::endl;
2018 block = emergeBlock(blockpos, false);
2021 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2025 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2029 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2031 v3s16 blockpos = getNodeBlockPos(p);
2032 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2033 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2035 infostream<<"Map::setNodeMetadata(): Need to emerge "
2036 <<PP(blockpos)<<std::endl;
2037 block = emergeBlock(blockpos, false);
2040 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2044 block->m_node_metadata.set(p_rel, meta);
2048 void Map::removeNodeMetadata(v3s16 p)
2050 v3s16 blockpos = getNodeBlockPos(p);
2051 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2052 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2055 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2059 block->m_node_metadata.remove(p_rel);
2062 NodeTimer Map::getNodeTimer(v3s16 p)
2064 v3s16 blockpos = getNodeBlockPos(p);
2065 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2066 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2068 infostream<<"Map::getNodeTimer(): Need to emerge "
2069 <<PP(blockpos)<<std::endl;
2070 block = emergeBlock(blockpos, false);
2073 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2077 NodeTimer t = block->m_node_timers.get(p_rel);
2081 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2083 v3s16 blockpos = getNodeBlockPos(p);
2084 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2085 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2087 infostream<<"Map::setNodeTimer(): Need to emerge "
2088 <<PP(blockpos)<<std::endl;
2089 block = emergeBlock(blockpos, false);
2092 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2096 block->m_node_timers.set(p_rel, t);
2099 void Map::removeNodeTimer(v3s16 p)
2101 v3s16 blockpos = getNodeBlockPos(p);
2102 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2103 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2106 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2110 block->m_node_timers.remove(p_rel);
2116 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2117 Map(dout_server, gamedef),
2119 m_map_metadata_changed(true)
2121 verbosestream<<__FUNCTION_NAME<<std::endl;
2124 Try to load map; if not found, create a new one.
2127 // Determine which database backend to use
2128 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2130 bool succeeded = conf.readConfigFile(conf_path.c_str());
2131 if (!succeeded || !conf.exists("backend")) {
2132 // fall back to sqlite3
2133 conf.set("backend", "sqlite3");
2135 std::string backend = conf.get("backend");
2136 dbase = createDatabase(backend, savedir, conf);
2138 if (!conf.updateConfigFile(conf_path.c_str()))
2139 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2141 m_savedir = savedir;
2142 m_map_saving_enabled = false;
2146 // If directory exists, check contents and load if possible
2147 if(fs::PathExists(m_savedir))
2149 // If directory is empty, it is safe to save into it.
2150 if(fs::GetDirListing(m_savedir).size() == 0)
2152 infostream<<"ServerMap: Empty save directory is valid."
2154 m_map_saving_enabled = true;
2159 // Load map metadata (seed, chunksize)
2162 catch(SettingNotFoundException &e){
2163 infostream<<"ServerMap: Some metadata not found."
2164 <<" Using default settings."<<std::endl;
2166 catch(FileNotGoodException &e){
2167 infostream<<"WARNING: Could not load map metadata"
2168 //<<" Disabling chunk-based generator."
2173 infostream<<"ServerMap: Successfully loaded map "
2174 <<"metadata from "<<savedir
2175 <<", assuming valid save directory."
2176 <<" seed="<< m_emerge->params.seed <<"."
2179 m_map_saving_enabled = true;
2180 // Map loaded, not creating new one
2184 // If directory doesn't exist, it is safe to save to it
2186 m_map_saving_enabled = true;
2189 catch(std::exception &e)
2191 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2192 <<", exception: "<<e.what()<<std::endl;
2193 infostream<<"Please remove the map or fix it."<<std::endl;
2194 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2197 infostream<<"Initializing new map."<<std::endl;
2199 // Create zero sector
2200 emergeSector(v2s16(0,0));
2202 // Initially write whole map
2203 save(MOD_STATE_CLEAN);
2206 ServerMap::~ServerMap()
2208 verbosestream<<__FUNCTION_NAME<<std::endl;
2212 if(m_map_saving_enabled)
2214 // Save only changed parts
2215 save(MOD_STATE_WRITE_AT_UNLOAD);
2216 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2220 infostream<<"ServerMap: Map not saved"<<std::endl;
2223 catch(std::exception &e)
2225 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2226 <<", exception: "<<e.what()<<std::endl;
2230 Close database if it was opened
2238 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2239 for(; i.atEnd() == false; i++)
2241 MapChunk *chunk = i.getNode()->getValue();
2247 u64 ServerMap::getSeed()
2249 return m_emerge->params.seed;
2252 s16 ServerMap::getWaterLevel()
2254 return m_emerge->params.water_level;
2257 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2259 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2260 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2262 s16 chunksize = m_emerge->params.chunksize;
2263 s16 coffset = -chunksize / 2;
2264 v3s16 chunk_offset(coffset, coffset, coffset);
2265 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2266 v3s16 blockpos_min = blockpos_div * chunksize;
2267 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2268 blockpos_min += chunk_offset;
2269 blockpos_max += chunk_offset;
2271 v3s16 extra_borders(1,1,1);
2273 // Do nothing if not inside limits (+-1 because of neighbors)
2274 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2275 blockpos_over_limit(blockpos_max + extra_borders))
2278 data->seed = m_emerge->params.seed;
2279 data->blockpos_min = blockpos_min;
2280 data->blockpos_max = blockpos_max;
2281 data->blockpos_requested = blockpos;
2282 data->nodedef = m_gamedef->ndef();
2285 Create the whole area of this and the neighboring blocks
2288 //TimeTaker timer("initBlockMake() create area");
2290 for(s16 x=blockpos_min.X-extra_borders.X;
2291 x<=blockpos_max.X+extra_borders.X; x++)
2292 for(s16 z=blockpos_min.Z-extra_borders.Z;
2293 z<=blockpos_max.Z+extra_borders.Z; z++)
2295 v2s16 sectorpos(x, z);
2296 // Sector metadata is loaded from disk if not already loaded.
2297 ServerMapSector *sector = createSector(sectorpos);
2298 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2301 for(s16 y=blockpos_min.Y-extra_borders.Y;
2302 y<=blockpos_max.Y+extra_borders.Y; y++)
2305 //MapBlock *block = createBlock(p);
2306 // 1) get from memory, 2) load from disk
2307 MapBlock *block = emergeBlock(p, false);
2308 // 3) create a blank one
2311 block = createBlock(p);
2314 Block gets sunlight if this is true.
2316 Refer to the map generator heuristics.
2318 bool ug = m_emerge->isBlockUnderground(p);
2319 block->setIsUnderground(ug);
2322 // Lighting will not be valid after make_chunk is called
2323 block->setLightingExpired(true);
2324 // Lighting will be calculated
2325 //block->setLightingExpired(false);
2331 Now we have a big empty area.
2333 Make a ManualMapVoxelManipulator that contains this and the
2337 // The area that contains this block and it's neighbors
2338 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2339 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2341 data->vmanip = new MMVManip(this);
2342 //data->vmanip->setMap(this);
2346 //TimeTaker timer("initBlockMake() initialEmerge");
2347 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2350 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2351 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2352 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2353 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2354 core::map<v3s16, u8>::Node *n;
2355 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2358 u8 flags = n->getValue();
2359 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2365 // Data is ready now.
2369 void ServerMap::finishBlockMake(BlockMakeData *data,
2370 std::map<v3s16, MapBlock*> &changed_blocks)
2372 v3s16 blockpos_min = data->blockpos_min;
2373 v3s16 blockpos_max = data->blockpos_max;
2374 v3s16 blockpos_requested = data->blockpos_requested;
2375 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2376 <<blockpos_requested.Y<<","
2377 <<blockpos_requested.Z<<")"<<std::endl;*/
2379 v3s16 extra_borders(1,1,1);
2381 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2383 /*infostream<<"Resulting vmanip:"<<std::endl;
2384 data->vmanip.print(infostream);*/
2386 // Make sure affected blocks are loaded
2387 for(s16 x=blockpos_min.X-extra_borders.X;
2388 x<=blockpos_max.X+extra_borders.X; x++)
2389 for(s16 z=blockpos_min.Z-extra_borders.Z;
2390 z<=blockpos_max.Z+extra_borders.Z; z++)
2391 for(s16 y=blockpos_min.Y-extra_borders.Y;
2392 y<=blockpos_max.Y+extra_borders.Y; y++)
2395 // Load from disk if not already in memory
2396 emergeBlock(p, false);
2400 Blit generated stuff to map
2401 NOTE: blitBackAll adds nearly everything to changed_blocks
2405 //TimeTaker timer("finishBlockMake() blitBackAll");
2406 data->vmanip->blitBackAll(&changed_blocks);
2409 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2412 Copy transforming liquid information
2414 while(data->transforming_liquid.size() > 0)
2416 m_transforming_liquid.push_back(data->transforming_liquid.front());
2417 data->transforming_liquid.pop_front();
2421 Do stuff in central blocks
2429 TimeTaker t("finishBlockMake lighting update");
2431 core::map<v3s16, MapBlock*> lighting_update_blocks;
2434 for(s16 x=blockpos_min.X-extra_borders.X;
2435 x<=blockpos_max.X+extra_borders.X; x++)
2436 for(s16 z=blockpos_min.Z-extra_borders.Z;
2437 z<=blockpos_max.Z+extra_borders.Z; z++)
2438 for(s16 y=blockpos_min.Y-extra_borders.Y;
2439 y<=blockpos_max.Y+extra_borders.Y; y++)
2442 MapBlock *block = getBlockNoCreateNoEx(p);
2444 lighting_update_blocks.insert(block->getPos(), block);
2447 updateLighting(lighting_update_blocks, changed_blocks);
2451 Set lighting to non-expired state in all of them.
2452 This is cheating, but it is not fast enough if all of them
2453 would actually be updated.
2455 for(s16 x=blockpos_min.X-extra_borders.X;
2456 x<=blockpos_max.X+extra_borders.X; x++)
2457 for(s16 z=blockpos_min.Z-extra_borders.Z;
2458 z<=blockpos_max.Z+extra_borders.Z; z++)
2459 for(s16 y=blockpos_min.Y-extra_borders.Y;
2460 y<=blockpos_max.Y+extra_borders.Y; y++)
2463 MapBlock * block = getBlockNoCreateNoEx(p);
2465 block->setLightingExpired(false);
2469 if(enable_mapgen_debug_info == false)
2470 t.stop(true); // Hide output
2475 Go through changed blocks
2477 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2478 i != changed_blocks.end(); ++i)
2480 MapBlock *block = i->second;
2484 Update day/night difference cache of the MapBlocks
2486 block->expireDayNightDiff();
2488 Set block as modified
2490 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2491 MOD_REASON_EXPIRE_DAYNIGHTDIFF);
2495 Set central blocks as generated
2497 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2498 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2499 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2502 MapBlock *block = getBlockNoCreateNoEx(p);
2505 block->setGenerated(true);
2509 Save changed parts of map
2510 NOTE: Will be saved later.
2512 //save(MOD_STATE_WRITE_AT_UNLOAD);
2514 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2515 <<","<<blockpos_requested.Y<<","
2516 <<blockpos_requested.Z<<")"<<std::endl;*/
2520 if(enable_mapgen_debug_info)
2523 Analyze resulting blocks
2525 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2526 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2527 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2528 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2529 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2530 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2532 v3s16 p = v3s16(x,y,z);
2533 MapBlock *block = getBlockNoCreateNoEx(p);
2535 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2536 infostream<<"Generated "<<spos<<": "
2537 <<analyze_block(block)<<std::endl;
2542 getBlockNoCreateNoEx(blockpos_requested);
2545 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2547 DSTACKF("%s: p2d=(%d,%d)",
2552 Check if it exists already in memory
2554 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2559 Try to load it from disk (with blocks)
2561 //if(loadSectorFull(p2d) == true)
2564 Try to load metadata from disk
2567 if(loadSectorMeta(p2d) == true)
2569 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2572 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2573 throw InvalidPositionException("");
2579 Do not create over-limit
2581 const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
2582 g_settings->getU16("map_generation_limit"));
2583 if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
2584 || p2d.X > map_gen_limit / MAP_BLOCKSIZE
2585 || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
2586 || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
2587 throw InvalidPositionException("createSector(): pos. over limit");
2590 Generate blank sector
2593 sector = new ServerMapSector(this, p2d, m_gamedef);
2595 // Sector position on map in nodes
2596 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2601 m_sectors[p2d] = sector;
2608 This is a quick-hand function for calling makeBlock().
2610 MapBlock * ServerMap::generateBlock(
2612 std::map<v3s16, MapBlock*> &modified_blocks
2615 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2617 /*infostream<<"generateBlock(): "
2618 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2621 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2623 TimeTaker timer("generateBlock");
2625 //MapBlock *block = original_dummy;
2627 v2s16 p2d(p.X, p.Z);
2628 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2631 Do not generate over-limit
2633 if(blockpos_over_limit(p))
2635 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2636 throw InvalidPositionException("generateBlock(): pos. over limit");
2640 Create block make data
2643 initBlockMake(&data, p);
2649 TimeTaker t("mapgen::make_block()");
2650 mapgen->makeChunk(&data);
2651 //mapgen::make_block(&data);
2653 if(enable_mapgen_debug_info == false)
2654 t.stop(true); // Hide output
2658 Blit data back on map, update lighting, add mobs and whatever this does
2660 finishBlockMake(&data, modified_blocks);
2665 MapBlock *block = getBlockNoCreateNoEx(p);
2673 bool erroneus_content = false;
2674 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2675 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2676 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2679 MapNode n = block->getNode(p);
2680 if(n.getContent() == CONTENT_IGNORE)
2682 infostream<<"CONTENT_IGNORE at "
2683 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2685 erroneus_content = true;
2689 if(erroneus_content)
2698 Generate a completely empty block
2702 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2703 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2705 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2708 n.setContent(CONTENT_AIR);
2709 block->setNode(v3s16(x0,y0,z0), n);
2715 if(enable_mapgen_debug_info == false)
2716 timer.stop(true); // Hide output
2722 MapBlock * ServerMap::createBlock(v3s16 p)
2724 DSTACKF("%s: p=(%d,%d,%d)",
2725 __FUNCTION_NAME, p.X, p.Y, p.Z);
2728 Do not create over-limit
2730 if (blockpos_over_limit(p))
2731 throw InvalidPositionException("createBlock(): pos. over limit");
2733 v2s16 p2d(p.X, p.Z);
2736 This will create or load a sector if not found in memory.
2737 If block exists on disk, it will be loaded.
2739 NOTE: On old save formats, this will be slow, as it generates
2740 lighting on blocks for them.
2742 ServerMapSector *sector;
2744 sector = (ServerMapSector*)createSector(p2d);
2745 assert(sector->getId() == MAPSECTOR_SERVER);
2747 catch(InvalidPositionException &e)
2749 infostream<<"createBlock: createSector() failed"<<std::endl;
2753 NOTE: This should not be done, or at least the exception
2754 should not be passed on as std::exception, because it
2755 won't be catched at all.
2757 /*catch(std::exception &e)
2759 infostream<<"createBlock: createSector() failed: "
2760 <<e.what()<<std::endl;
2765 Try to get a block from the sector
2768 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2771 if(block->isDummy())
2776 block = sector->createBlankBlock(block_y);
2781 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2783 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2785 p.X, p.Y, p.Z, create_blank);
2788 MapBlock *block = getBlockNoCreateNoEx(p);
2789 if(block && block->isDummy() == false)
2794 MapBlock *block = loadBlock(p);
2800 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2801 MapBlock *block = sector->createBlankBlock(p.Y);
2809 std::map<v3s16, MapBlock*> modified_blocks;
2810 MapBlock *block = generateBlock(p, modified_blocks);
2814 event.type = MEET_OTHER;
2817 // Copy modified_blocks to event
2818 for(std::map<v3s16, MapBlock*>::iterator
2819 i = modified_blocks.begin();
2820 i != modified_blocks.end(); ++i)
2822 event.modified_blocks.insert(i->first);
2826 dispatchEvent(&event);
2836 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2838 MapBlock *block = getBlockNoCreateNoEx(p3d);
2840 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2845 void ServerMap::prepareBlock(MapBlock *block) {
2848 // N.B. This requires no synchronization, since data will not be modified unless
2849 // the VoxelManipulator being updated belongs to the same thread.
2850 void ServerMap::updateVManip(v3s16 pos)
2852 Mapgen *mg = m_emerge->getCurrentMapgen();
2856 MMVManip *vm = mg->vm;
2860 if (!vm->m_area.contains(pos))
2863 s32 idx = vm->m_area.index(pos);
2864 vm->m_data[idx] = getNodeNoEx(pos);
2865 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2867 vm->m_is_dirty = true;
2870 s16 ServerMap::findGroundLevel(v2s16 p2d)
2874 Uh, just do something random...
2876 // Find existing map from top to down
2879 v3s16 p(p2d.X, max, p2d.Y);
2880 for(; p.Y>min; p.Y--)
2882 MapNode n = getNodeNoEx(p);
2883 if(n.getContent() != CONTENT_IGNORE)
2888 // If this node is not air, go to plan b
2889 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2891 // Search existing walkable and return it
2892 for(; p.Y>min; p.Y--)
2894 MapNode n = getNodeNoEx(p);
2895 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2904 Determine from map generator noise functions
2907 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2910 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2911 //return (s16)level;
2914 bool ServerMap::loadFromFolders() {
2915 if (!dbase->initialized() &&
2916 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2921 void ServerMap::createDirs(std::string path)
2923 if(fs::CreateAllDirs(path) == false)
2925 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2926 <<"\""<<path<<"\""<<std::endl;
2927 throw BaseException("ServerMap failed to create directory");
2931 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2937 snprintf(cc, 9, "%.4x%.4x",
2938 (unsigned int) pos.X & 0xffff,
2939 (unsigned int) pos.Y & 0xffff);
2941 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2943 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2944 (unsigned int) pos.X & 0xfff,
2945 (unsigned int) pos.Y & 0xfff);
2947 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2954 v2s16 ServerMap::getSectorPos(std::string dirname)
2956 unsigned int x = 0, y = 0;
2958 std::string component;
2959 fs::RemoveLastPathComponent(dirname, &component, 1);
2960 if(component.size() == 8)
2963 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2965 else if(component.size() == 3)
2968 fs::RemoveLastPathComponent(dirname, &component, 2);
2969 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2970 // Sign-extend the 12 bit values up to 16 bits...
2971 if(x & 0x800) x |= 0xF000;
2972 if(y & 0x800) y |= 0xF000;
2979 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2980 v2s16 pos((s16)x, (s16)y);
2984 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2986 v2s16 p2d = getSectorPos(sectordir);
2988 if(blockfile.size() != 4){
2989 throw InvalidFilenameException("Invalid block filename");
2992 int r = sscanf(blockfile.c_str(), "%4x", &y);
2994 throw InvalidFilenameException("Invalid block filename");
2995 return v3s16(p2d.X, y, p2d.Y);
2998 std::string ServerMap::getBlockFilename(v3s16 p)
3001 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3005 void ServerMap::save(ModifiedState save_level)
3007 DSTACK(__FUNCTION_NAME);
3008 if(m_map_saving_enabled == false) {
3009 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3013 if(save_level == MOD_STATE_CLEAN)
3014 infostream<<"ServerMap: Saving whole map, this can take time."
3017 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
3021 // Profile modified reasons
3022 Profiler modprofiler;
3024 u32 sector_meta_count = 0;
3025 u32 block_count = 0;
3026 u32 block_count_all = 0; // Number of blocks in memory
3028 // Don't do anything with sqlite unless something is really saved
3029 bool save_started = false;
3031 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3032 i != m_sectors.end(); ++i) {
3033 ServerMapSector *sector = (ServerMapSector*)i->second;
3034 assert(sector->getId() == MAPSECTOR_SERVER);
3036 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
3037 saveSectorMeta(sector);
3038 sector_meta_count++;
3041 MapBlockVect blocks;
3042 sector->getBlocks(blocks);
3044 for(MapBlockVect::iterator j = blocks.begin();
3045 j != blocks.end(); ++j) {
3046 MapBlock *block = *j;
3050 if(block->getModified() >= (u32)save_level) {
3054 save_started = true;
3057 modprofiler.add(block->getModifiedReasonString(), 1);
3062 /*infostream<<"ServerMap: Written block ("
3063 <<block->getPos().X<<","
3064 <<block->getPos().Y<<","
3065 <<block->getPos().Z<<")"
3075 Only print if something happened or saved whole map
3077 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3078 || block_count != 0) {
3079 infostream<<"ServerMap: Written: "
3080 <<sector_meta_count<<" sector metadata files, "
3081 <<block_count<<" block files"
3082 <<", "<<block_count_all<<" blocks in memory."
3084 PrintInfo(infostream); // ServerMap/ClientMap:
3085 infostream<<"Blocks modified by: "<<std::endl;
3086 modprofiler.print(infostream);
3090 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
3092 if (loadFromFolders()) {
3093 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
3094 << "all blocks that are stored in flat files." << std::endl;
3096 dbase->listAllLoadableBlocks(dst);
3099 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
3101 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3102 si != m_sectors.end(); ++si)
3104 MapSector *sector = si->second;
3106 MapBlockVect blocks;
3107 sector->getBlocks(blocks);
3109 for(MapBlockVect::iterator i = blocks.begin();
3110 i != blocks.end(); ++i) {
3111 v3s16 p = (*i)->getPos();
3117 void ServerMap::saveMapMeta()
3119 DSTACK(__FUNCTION_NAME);
3121 createDirs(m_savedir);
3123 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3124 std::ostringstream oss(std::ios_base::binary);
3127 m_emerge->params.save(conf);
3128 conf.writeLines(oss);
3130 oss << "[end_of_params]\n";
3132 if(!fs::safeWriteToFile(fullpath, oss.str())) {
3133 errorstream << "ServerMap::saveMapMeta(): "
3134 << "could not write " << fullpath << std::endl;
3135 throw FileNotGoodException("Cannot save chunk metadata");
3138 m_map_metadata_changed = false;
3141 void ServerMap::loadMapMeta()
3143 DSTACK(__FUNCTION_NAME);
3146 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3148 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3150 errorstream << "ServerMap::loadMapMeta(): "
3151 "could not open " << fullpath << std::endl;
3152 throw FileNotGoodException("Cannot open map metadata");
3155 if (!conf.parseConfigLines(is, "[end_of_params]")) {
3156 throw SerializationError("ServerMap::loadMapMeta(): "
3157 "[end_of_params] not found!");
3160 m_emerge->params.load(conf);
3162 verbosestream << "ServerMap::loadMapMeta(): seed="
3163 << m_emerge->params.seed << std::endl;
3166 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3168 DSTACK(__FUNCTION_NAME);
3169 // Format used for writing
3170 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3172 v2s16 pos = sector->getPos();
3173 std::string dir = getSectorDir(pos);
3176 std::string fullpath = dir + DIR_DELIM + "meta";
3177 std::ostringstream ss(std::ios_base::binary);
3179 sector->serialize(ss, version);
3181 if(!fs::safeWriteToFile(fullpath, ss.str()))
3182 throw FileNotGoodException("Cannot write sector metafile");
3184 sector->differs_from_disk = false;
3187 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3189 DSTACK(__FUNCTION_NAME);
3191 v2s16 p2d = getSectorPos(sectordir);
3193 ServerMapSector *sector = NULL;
3195 std::string fullpath = sectordir + DIR_DELIM + "meta";
3196 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3197 if(is.good() == false)
3199 // If the directory exists anyway, it probably is in some old
3200 // format. Just go ahead and create the sector.
3201 if(fs::PathExists(sectordir))
3203 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3204 <<fullpath<<" doesn't exist but directory does."
3205 <<" Continuing with a sector with no metadata."
3207 sector = new ServerMapSector(this, p2d, m_gamedef);
3208 m_sectors[p2d] = sector;
3212 throw FileNotGoodException("Cannot open sector metafile");
3217 sector = ServerMapSector::deSerialize
3218 (is, this, p2d, m_sectors, m_gamedef);
3220 saveSectorMeta(sector);
3223 sector->differs_from_disk = false;
3228 bool ServerMap::loadSectorMeta(v2s16 p2d)
3230 DSTACK(__FUNCTION_NAME);
3232 // The directory layout we're going to load from.
3233 // 1 - original sectors/xxxxzzzz/
3234 // 2 - new sectors2/xxx/zzz/
3235 // If we load from anything but the latest structure, we will
3236 // immediately save to the new one, and remove the old.
3238 std::string sectordir1 = getSectorDir(p2d, 1);
3239 std::string sectordir;
3240 if(fs::PathExists(sectordir1))
3242 sectordir = sectordir1;
3247 sectordir = getSectorDir(p2d, 2);
3251 loadSectorMeta(sectordir, loadlayout != 2);
3253 catch(InvalidFilenameException &e)
3257 catch(FileNotGoodException &e)
3261 catch(std::exception &e)
3270 bool ServerMap::loadSectorFull(v2s16 p2d)
3272 DSTACK(__FUNCTION_NAME);
3274 MapSector *sector = NULL;
3276 // The directory layout we're going to load from.
3277 // 1 - original sectors/xxxxzzzz/
3278 // 2 - new sectors2/xxx/zzz/
3279 // If we load from anything but the latest structure, we will
3280 // immediately save to the new one, and remove the old.
3282 std::string sectordir1 = getSectorDir(p2d, 1);
3283 std::string sectordir;
3284 if(fs::PathExists(sectordir1))
3286 sectordir = sectordir1;
3291 sectordir = getSectorDir(p2d, 2);
3295 sector = loadSectorMeta(sectordir, loadlayout != 2);
3297 catch(InvalidFilenameException &e)
3301 catch(FileNotGoodException &e)
3305 catch(std::exception &e)
3313 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3315 std::vector<fs::DirListNode>::iterator i2;
3316 for(i2=list2.begin(); i2!=list2.end(); i2++)
3322 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3324 catch(InvalidFilenameException &e)
3326 // This catches unknown crap in directory
3332 infostream<<"Sector converted to new layout - deleting "<<
3333 sectordir1<<std::endl;
3334 fs::RecursiveDelete(sectordir1);
3341 Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
3343 if (name == "sqlite3")
3344 return new Database_SQLite3(savedir);
3345 if (name == "dummy")
3346 return new Database_Dummy();
3348 else if (name == "leveldb")
3349 return new Database_LevelDB(savedir);
3352 else if (name == "redis")
3353 return new Database_Redis(conf);
3356 throw BaseException(std::string("Database backend ") + name + " not supported.");
3359 void ServerMap::beginSave()
3364 void ServerMap::endSave()
3369 bool ServerMap::saveBlock(MapBlock *block)
3371 return saveBlock(block, dbase);
3374 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3376 v3s16 p3d = block->getPos();
3378 // Dummy blocks are not written
3379 if (block->isDummy()) {
3380 errorstream << "WARNING: saveBlock: Not writing dummy block "
3381 << PP(p3d) << std::endl;
3385 // Format used for writing
3386 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3389 [0] u8 serialization version
3392 std::ostringstream o(std::ios_base::binary);
3393 o.write((char*) &version, 1);
3394 block->serialize(o, version, true);
3396 std::string data = o.str();
3397 bool ret = db->saveBlock(p3d, data);
3399 // We just wrote it to the disk so clear modified flag
3400 block->resetModified();
3405 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3406 MapSector *sector, bool save_after_load)
3408 DSTACK(__FUNCTION_NAME);
3410 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3413 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3414 if(is.good() == false)
3415 throw FileNotGoodException("Cannot open block file");
3417 v3s16 p3d = getBlockPos(sectordir, blockfile);
3418 v2s16 p2d(p3d.X, p3d.Z);
3420 assert(sector->getPos() == p2d);
3422 u8 version = SER_FMT_VER_INVALID;
3423 is.read((char*)&version, 1);
3426 throw SerializationError("ServerMap::loadBlock(): Failed"
3427 " to read MapBlock version");
3429 /*u32 block_size = MapBlock::serializedLength(version);
3430 SharedBuffer<u8> data(block_size);
3431 is.read((char*)*data, block_size);*/
3433 // This will always return a sector because we're the server
3434 //MapSector *sector = emergeSector(p2d);
3436 MapBlock *block = NULL;
3437 bool created_new = false;
3438 block = sector->getBlockNoCreateNoEx(p3d.Y);
3441 block = sector->createBlankBlockNoInsert(p3d.Y);
3446 block->deSerialize(is, version, true);
3448 // If it's a new block, insert it to the map
3450 sector->insertBlock(block);
3453 Save blocks loaded in old format in new format
3456 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3460 // Should be in database now, so delete the old file
3461 fs::RecursiveDelete(fullpath);
3464 // We just loaded it from the disk, so it's up-to-date.
3465 block->resetModified();
3468 catch(SerializationError &e)
3470 infostream<<"WARNING: Invalid block data on disk "
3471 <<"fullpath="<<fullpath
3472 <<" (SerializationError). "
3473 <<"what()="<<e.what()
3475 // Ignoring. A new one will be generated.
3478 // TODO: Backup file; name is in fullpath.
3482 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3484 DSTACK(__FUNCTION_NAME);
3487 std::istringstream is(*blob, std::ios_base::binary);
3489 u8 version = SER_FMT_VER_INVALID;
3490 is.read((char*)&version, 1);
3493 throw SerializationError("ServerMap::loadBlock(): Failed"
3494 " to read MapBlock version");
3496 /*u32 block_size = MapBlock::serializedLength(version);
3497 SharedBuffer<u8> data(block_size);
3498 is.read((char*)*data, block_size);*/
3500 // This will always return a sector because we're the server
3501 //MapSector *sector = emergeSector(p2d);
3503 MapBlock *block = NULL;
3504 bool created_new = false;
3505 block = sector->getBlockNoCreateNoEx(p3d.Y);
3508 block = sector->createBlankBlockNoInsert(p3d.Y);
3513 block->deSerialize(is, version, true);
3515 // If it's a new block, insert it to the map
3517 sector->insertBlock(block);
3520 Save blocks loaded in old format in new format
3523 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3524 // Only save if asked to; no need to update version
3528 // We just loaded it from, so it's up-to-date.
3529 block->resetModified();
3532 catch(SerializationError &e)
3534 errorstream<<"Invalid block data in database"
3535 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3536 <<" (SerializationError): "<<e.what()<<std::endl;
3538 // TODO: Block should be marked as invalid in memory so that it is
3539 // not touched but the game can run
3541 if(g_settings->getBool("ignore_world_load_errors")){
3542 errorstream<<"Ignoring block load error. Duck and cover! "
3543 <<"(ignore_world_load_errors)"<<std::endl;
3545 throw SerializationError("Invalid block data in database");
3550 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3552 DSTACK(__FUNCTION_NAME);
3554 v2s16 p2d(blockpos.X, blockpos.Z);
3558 ret = dbase->loadBlock(blockpos);
3560 loadBlock(&ret, blockpos, createSector(p2d), false);
3561 return getBlockNoCreateNoEx(blockpos);
3563 // Not found in database, try the files
3565 // The directory layout we're going to load from.
3566 // 1 - original sectors/xxxxzzzz/
3567 // 2 - new sectors2/xxx/zzz/
3568 // If we load from anything but the latest structure, we will
3569 // immediately save to the new one, and remove the old.
3571 std::string sectordir1 = getSectorDir(p2d, 1);
3572 std::string sectordir;
3573 if(fs::PathExists(sectordir1))
3575 sectordir = sectordir1;
3580 sectordir = getSectorDir(p2d, 2);
3584 Make sure sector is loaded
3586 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3590 sector = loadSectorMeta(sectordir, loadlayout != 2);
3592 catch(InvalidFilenameException &e)
3596 catch(FileNotGoodException &e)
3600 catch(std::exception &e)
3607 Make sure file exists
3610 std::string blockfilename = getBlockFilename(blockpos);
3611 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3615 Load block and save it to the database
3617 loadBlock(sectordir, blockfilename, sector, true);
3618 return getBlockNoCreateNoEx(blockpos);
3621 bool ServerMap::deleteBlock(v3s16 blockpos)
3623 if (!dbase->deleteBlock(blockpos))
3626 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3628 v2s16 p2d(blockpos.X, blockpos.Z);
3629 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3632 sector->deleteBlock(block);
3638 void ServerMap::PrintInfo(std::ostream &out)
3643 MMVManip::MMVManip(Map *map):
3646 m_create_area(false),
3651 MMVManip::~MMVManip()
3655 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3656 bool load_if_inexistent)
3658 TimeTaker timer1("initialEmerge", &emerge_time);
3660 // Units of these are MapBlocks
3661 v3s16 p_min = blockpos_min;
3662 v3s16 p_max = blockpos_max;
3664 VoxelArea block_area_nodes
3665 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3667 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3670 infostream<<"initialEmerge: area: ";
3671 block_area_nodes.print(infostream);
3672 infostream<<" ("<<size_MB<<"MB)";
3673 infostream<<std::endl;
3676 addArea(block_area_nodes);
3678 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3679 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3680 for(s32 x=p_min.X; x<=p_max.X; x++)
3685 std::map<v3s16, u8>::iterator n;
3686 n = m_loaded_blocks.find(p);
3687 if(n != m_loaded_blocks.end())
3690 bool block_data_inexistent = false;
3693 TimeTaker timer1("emerge load", &emerge_load_time);
3695 block = m_map->getBlockNoCreate(p);
3696 if(block->isDummy())
3697 block_data_inexistent = true;
3699 block->copyTo(*this);
3701 catch(InvalidPositionException &e)
3703 block_data_inexistent = true;
3706 if(block_data_inexistent)
3709 if (load_if_inexistent) {
3710 ServerMap *svrmap = (ServerMap *)m_map;
3711 block = svrmap->emergeBlock(p, false);
3713 block = svrmap->createBlock(p);
3714 block->copyTo(*this);
3716 flags |= VMANIP_BLOCK_DATA_INEXIST;
3719 Mark area inexistent
3721 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3722 // Fill with VOXELFLAG_NO_DATA
3723 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3724 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3726 s32 i = m_area.index(a.MinEdge.X,y,z);
3727 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3731 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3733 // Mark that block was loaded as blank
3734 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3737 m_loaded_blocks[p] = flags;
3743 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3744 bool overwrite_generated)
3746 if(m_area.getExtent() == v3s16(0,0,0))
3750 Copy data of all blocks
3752 for(std::map<v3s16, u8>::iterator
3753 i = m_loaded_blocks.begin();
3754 i != m_loaded_blocks.end(); ++i)
3757 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3758 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3759 if ((existed == false) || (block == NULL) ||
3760 (overwrite_generated == false && block->isGenerated() == true))
3763 block->copyFrom(*this);
3766 (*modified_blocks)[p] = block;