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"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
48 #include "database-leveldb.h"
51 #include "database-redis.h"
54 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
57 SQLite format specification:
58 - Initially only replaces sectors/ and sectors2/
60 If map.sqlite does not exist in the save dir
61 or the block was not found in the database
62 the map will try to load from sectors folder.
63 In either case, map.sqlite will be created
64 and all future saves will save there.
66 Structure of map.sqlite:
77 Map::Map(std::ostream &dout, IGameDef *gamedef):
81 m_transforming_liquid_loop_count_multiplier(1.0f),
82 m_unprocessed_count(0),
83 m_inc_trending_up_start_time(0),
84 m_queue_size_timer_started(false)
93 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
94 i != m_sectors.end(); ++i)
100 void Map::addEventReceiver(MapEventReceiver *event_receiver)
102 m_event_receivers.insert(event_receiver);
105 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
107 m_event_receivers.erase(event_receiver);
110 void Map::dispatchEvent(MapEditEvent *event)
112 for(std::set<MapEventReceiver*>::iterator
113 i = m_event_receivers.begin();
114 i != m_event_receivers.end(); ++i)
116 (*i)->onMapEditEvent(event);
120 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
122 if(m_sector_cache != NULL && p == m_sector_cache_p){
123 MapSector * sector = m_sector_cache;
127 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
129 if(n == m_sectors.end())
132 MapSector *sector = n->second;
134 // Cache the last result
135 m_sector_cache_p = p;
136 m_sector_cache = sector;
141 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
143 return getSectorNoGenerateNoExNoLock(p);
146 MapSector * Map::getSectorNoGenerate(v2s16 p)
148 MapSector *sector = getSectorNoGenerateNoEx(p);
150 throw InvalidPositionException();
155 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
157 v2s16 p2d(p3d.X, p3d.Z);
158 MapSector * sector = getSectorNoGenerateNoEx(p2d);
161 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
165 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
167 MapBlock *block = getBlockNoCreateNoEx(p3d);
169 throw InvalidPositionException();
173 bool Map::isNodeUnderground(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
177 MapBlock * block = getBlockNoCreate(blockpos);
178 return block->getIsUnderground();
180 catch(InvalidPositionException &e)
186 bool Map::isValidPosition(v3s16 p)
188 v3s16 blockpos = getNodeBlockPos(p);
189 MapBlock *block = getBlockNoCreate(blockpos);
190 return (block != NULL);
193 // Returns a CONTENT_IGNORE node if not found
194 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
196 v3s16 blockpos = getNodeBlockPos(p);
197 MapBlock *block = getBlockNoCreateNoEx(blockpos);
199 if (is_valid_position != NULL)
200 *is_valid_position = false;
201 return MapNode(CONTENT_IGNORE);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
207 if (is_valid_position != NULL)
208 *is_valid_position = is_valid_p;
214 // throws InvalidPositionException if not found
215 // TODO: Now this is deprecated, getNodeNoEx should be renamed
216 MapNode Map::getNode(v3s16 p)
218 v3s16 blockpos = getNodeBlockPos(p);
219 MapBlock *block = getBlockNoCreateNoEx(blockpos);
221 throw InvalidPositionException();
222 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
223 bool is_valid_position;
224 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
225 if (!is_valid_position)
226 throw InvalidPositionException();
231 // throws InvalidPositionException if not found
232 void Map::setNode(v3s16 p, MapNode & n)
234 v3s16 blockpos = getNodeBlockPos(p);
235 MapBlock *block = getBlockNoCreate(blockpos);
236 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
237 // Never allow placing CONTENT_IGNORE, it fucks up stuff
238 if(n.getContent() == CONTENT_IGNORE){
240 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
241 <<" while trying to replace \""
242 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
243 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
244 debug_stacks_print_to(infostream);
247 block->setNodeNoCheck(relpos, n);
252 Goes recursively through the neighbours of the node.
254 Alters only transparent nodes.
256 If the lighting of the neighbour is lower than the lighting of
257 the node was (before changing it to 0 at the step before), the
258 lighting of the neighbour is set to 0 and then the same stuff
259 repeats for the neighbour.
261 The ending nodes of the routine are stored in light_sources.
262 This is useful when a light is removed. In such case, this
263 routine can be called for the light node and then again for
264 light_sources to re-light the area without the removed light.
266 values of from_nodes are lighting values.
268 void Map::unspreadLight(enum LightBank bank,
269 std::map<v3s16, u8> & from_nodes,
270 std::set<v3s16> & light_sources,
271 std::map<v3s16, MapBlock*> & modified_blocks)
273 INodeDefManager *nodemgr = m_gamedef->ndef();
276 v3s16(0,0,1), // back
278 v3s16(1,0,0), // right
279 v3s16(0,0,-1), // front
280 v3s16(0,-1,0), // bottom
281 v3s16(-1,0,0), // left
284 if(from_nodes.empty())
287 u32 blockchangecount = 0;
289 std::map<v3s16, u8> unlighted_nodes;
292 Initialize block cache
295 MapBlock *block = NULL;
296 // Cache this a bit, too
297 bool block_checked_in_modified = false;
299 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
300 j != from_nodes.end(); ++j)
302 v3s16 pos = j->first;
303 v3s16 blockpos = getNodeBlockPos(pos);
305 // Only fetch a new block if the block position has changed
307 if(block == NULL || blockpos != blockpos_last){
308 block = getBlockNoCreate(blockpos);
309 blockpos_last = blockpos;
311 block_checked_in_modified = false;
315 catch(InvalidPositionException &e)
323 // Calculate relative position in block
324 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
326 // Get node straight from the block
327 //MapNode n = block->getNode(relpos);
329 u8 oldlight = j->second;
331 // Loop through 6 neighbors
332 for(u16 i=0; i<6; i++)
334 // Get the position of the neighbor node
335 v3s16 n2pos = pos + dirs[i];
337 // Get the block where the node is located
338 v3s16 blockpos, relpos;
339 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
341 // Only fetch a new block if the block position has changed
343 if(block == NULL || blockpos != blockpos_last){
344 block = getBlockNoCreate(blockpos);
345 blockpos_last = blockpos;
347 block_checked_in_modified = false;
351 catch(InvalidPositionException &e) {
355 // Get node straight from the block
356 bool is_valid_position;
357 MapNode n2 = block->getNode(relpos, &is_valid_position);
358 if (!is_valid_position)
361 bool changed = false;
363 //TODO: Optimize output by optimizing light_sources?
366 If the neighbor is dimmer than what was specified
367 as oldlight (the light of the previous node)
369 if(n2.getLight(bank, nodemgr) < oldlight)
372 And the neighbor is transparent and it has some light
374 if(nodemgr->get(n2).light_propagates
375 && n2.getLight(bank, nodemgr) != 0)
378 Set light to 0 and add to queue
381 u8 current_light = n2.getLight(bank, nodemgr);
382 n2.setLight(bank, 0, nodemgr);
383 block->setNode(relpos, n2);
385 unlighted_nodes[n2pos] = current_light;
389 Remove from light_sources if it is there
390 NOTE: This doesn't happen nearly at all
392 /*if(light_sources.find(n2pos))
394 infostream<<"Removed from light_sources"<<std::endl;
395 light_sources.remove(n2pos);
400 if(light_sources.find(n2pos) != NULL)
401 light_sources.remove(n2pos);*/
404 light_sources.insert(n2pos);
407 // Add to modified_blocks
408 if(changed == true && block_checked_in_modified == false)
410 // If the block is not found in modified_blocks, add.
411 if(modified_blocks.find(blockpos) == modified_blocks.end())
413 modified_blocks[blockpos] = block;
415 block_checked_in_modified = true;
420 /*infostream<<"unspreadLight(): Changed block "
421 <<blockchangecount<<" times"
422 <<" for "<<from_nodes.size()<<" nodes"
425 if(!unlighted_nodes.empty())
426 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
430 A single-node wrapper of the above
432 void Map::unLightNeighbors(enum LightBank bank,
433 v3s16 pos, u8 lightwas,
434 std::set<v3s16> & light_sources,
435 std::map<v3s16, MapBlock*> & modified_blocks)
437 std::map<v3s16, u8> from_nodes;
438 from_nodes[pos] = lightwas;
440 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
444 Lights neighbors of from_nodes, collects all them and then
447 void Map::spreadLight(enum LightBank bank,
448 std::set<v3s16> & from_nodes,
449 std::map<v3s16, MapBlock*> & modified_blocks)
451 INodeDefManager *nodemgr = m_gamedef->ndef();
453 const v3s16 dirs[6] = {
454 v3s16(0,0,1), // back
456 v3s16(1,0,0), // right
457 v3s16(0,0,-1), // front
458 v3s16(0,-1,0), // bottom
459 v3s16(-1,0,0), // left
462 if(from_nodes.empty())
465 u32 blockchangecount = 0;
467 std::set<v3s16> lighted_nodes;
470 Initialize block cache
473 MapBlock *block = NULL;
474 // Cache this a bit, too
475 bool block_checked_in_modified = false;
477 for(std::set<v3s16>::iterator j = from_nodes.begin();
478 j != from_nodes.end(); ++j)
481 v3s16 blockpos, relpos;
483 getNodeBlockPosWithOffset(pos, blockpos, relpos);
485 // Only fetch a new block if the block position has changed
487 if(block == NULL || blockpos != blockpos_last){
488 block = getBlockNoCreate(blockpos);
489 blockpos_last = blockpos;
491 block_checked_in_modified = false;
495 catch(InvalidPositionException &e) {
502 // Get node straight from the block
503 bool is_valid_position;
504 MapNode n = block->getNode(relpos, &is_valid_position);
506 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
507 u8 newlight = diminish_light(oldlight);
509 // Loop through 6 neighbors
510 for(u16 i=0; i<6; i++){
511 // Get the position of the neighbor node
512 v3s16 n2pos = pos + dirs[i];
514 // Get the block where the node is located
515 v3s16 blockpos, relpos;
516 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
518 // Only fetch a new block if the block position has changed
520 if(block == NULL || blockpos != blockpos_last){
521 block = getBlockNoCreate(blockpos);
522 blockpos_last = blockpos;
524 block_checked_in_modified = false;
528 catch(InvalidPositionException &e) {
532 // Get node straight from the block
533 MapNode n2 = block->getNode(relpos, &is_valid_position);
534 if (!is_valid_position)
537 bool changed = false;
539 If the neighbor is brighter than the current node,
540 add to list (it will light up this node on its turn)
542 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
544 lighted_nodes.insert(n2pos);
548 If the neighbor is dimmer than how much light this node
549 would spread on it, add to list
551 if(n2.getLight(bank, nodemgr) < newlight)
553 if(nodemgr->get(n2).light_propagates)
555 n2.setLight(bank, newlight, nodemgr);
556 block->setNode(relpos, n2);
557 lighted_nodes.insert(n2pos);
562 // Add to modified_blocks
563 if(changed == true && block_checked_in_modified == false)
565 // If the block is not found in modified_blocks, add.
566 if(modified_blocks.find(blockpos) == modified_blocks.end())
568 modified_blocks[blockpos] = block;
570 block_checked_in_modified = true;
575 /*infostream<<"spreadLight(): Changed block "
576 <<blockchangecount<<" times"
577 <<" for "<<from_nodes.size()<<" nodes"
580 if(!lighted_nodes.empty())
581 spreadLight(bank, lighted_nodes, modified_blocks);
585 A single-node source variation of the above.
587 void Map::lightNeighbors(enum LightBank bank,
589 std::map<v3s16, MapBlock*> & modified_blocks)
591 std::set<v3s16> from_nodes;
592 from_nodes.insert(pos);
593 spreadLight(bank, from_nodes, modified_blocks);
596 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
598 INodeDefManager *nodemgr = m_gamedef->ndef();
601 v3s16(0,0,1), // back
603 v3s16(1,0,0), // right
604 v3s16(0,0,-1), // front
605 v3s16(0,-1,0), // bottom
606 v3s16(-1,0,0), // left
609 u8 brightest_light = 0;
610 v3s16 brightest_pos(0,0,0);
611 bool found_something = false;
613 // Loop through 6 neighbors
614 for(u16 i=0; i<6; i++){
615 // Get the position of the neighbor node
616 v3s16 n2pos = p + dirs[i];
618 bool is_valid_position;
619 n2 = getNodeNoEx(n2pos, &is_valid_position);
620 if (!is_valid_position)
623 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
624 brightest_light = n2.getLight(bank, nodemgr);
625 brightest_pos = n2pos;
626 found_something = true;
630 if(found_something == false)
631 throw InvalidPositionException();
633 return brightest_pos;
637 Propagates sunlight down from a node.
638 Starting point gets sunlight.
640 Returns the lowest y value of where the sunlight went.
642 Mud is turned into grass in where the sunlight stops.
644 s16 Map::propagateSunlight(v3s16 start,
645 std::map<v3s16, MapBlock*> & modified_blocks)
647 INodeDefManager *nodemgr = m_gamedef->ndef();
652 v3s16 pos(start.X, y, start.Z);
654 v3s16 blockpos = getNodeBlockPos(pos);
657 block = getBlockNoCreate(blockpos);
659 catch(InvalidPositionException &e)
664 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
665 bool is_valid_position;
666 MapNode n = block->getNode(relpos, &is_valid_position);
667 if (!is_valid_position)
670 if(nodemgr->get(n).sunlight_propagates)
672 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
673 block->setNode(relpos, n);
675 modified_blocks[blockpos] = block;
679 // Sunlight goes no further
686 void Map::updateLighting(enum LightBank bank,
687 std::map<v3s16, MapBlock*> & a_blocks,
688 std::map<v3s16, MapBlock*> & modified_blocks)
690 INodeDefManager *nodemgr = m_gamedef->ndef();
692 /*m_dout<<DTIME<<"Map::updateLighting(): "
693 <<a_blocks.size()<<" blocks."<<std::endl;*/
695 //TimeTaker timer("updateLighting");
699 //u32 count_was = modified_blocks.size();
701 //std::map<v3s16, MapBlock*> blocks_to_update;
703 std::set<v3s16> light_sources;
705 std::map<v3s16, u8> unlight_from;
707 int num_bottom_invalid = 0;
710 //TimeTaker t("first stuff");
712 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
713 i != a_blocks.end(); ++i)
715 MapBlock *block = i->second;
719 // Don't bother with dummy blocks.
723 v3s16 pos = block->getPos();
724 v3s16 posnodes = block->getPosRelative();
725 modified_blocks[pos] = block;
726 //blocks_to_update[pos] = block;
729 Clear all light from block
731 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
732 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
733 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
736 bool is_valid_position;
737 MapNode n = block->getNode(p, &is_valid_position);
738 if (!is_valid_position) {
739 /* This would happen when dealing with a
742 infostream<<"updateLighting(): InvalidPositionException"
746 u8 oldlight = n.getLight(bank, nodemgr);
747 n.setLight(bank, 0, nodemgr);
748 block->setNode(p, n);
750 // If node sources light, add to list
751 u8 source = nodemgr->get(n).light_source;
753 light_sources.insert(p + posnodes);
755 // Collect borders for unlighting
756 if((x==0 || x == MAP_BLOCKSIZE-1
757 || y==0 || y == MAP_BLOCKSIZE-1
758 || z==0 || z == MAP_BLOCKSIZE-1)
761 v3s16 p_map = p + posnodes;
762 unlight_from[p_map] = oldlight;
768 if(bank == LIGHTBANK_DAY)
770 bool bottom_valid = block->propagateSunlight(light_sources);
773 num_bottom_invalid++;
775 // If bottom is valid, we're done.
779 else if(bank == LIGHTBANK_NIGHT)
781 // For night lighting, sunlight is not propagated
786 // Invalid lighting bank
790 /*infostream<<"Bottom for sunlight-propagated block ("
791 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
794 // Bottom sunlight is not valid; get the block and loop to it
798 block = getBlockNoCreate(pos);
800 catch(InvalidPositionException &e)
811 Enable this to disable proper lighting for speeding up map
812 generation for testing or whatever
815 //if(g_settings->get(""))
817 core::map<v3s16, MapBlock*>::Iterator i;
818 i = blocks_to_update.getIterator();
819 for(; i.atEnd() == false; i++)
821 MapBlock *block = i.getNode()->getValue();
822 v3s16 p = block->getPos();
823 block->setLightingExpired(false);
831 //TimeTaker timer("unspreadLight");
832 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
837 u32 diff = modified_blocks.size() - count_was;
838 count_was = modified_blocks.size();
839 infostream<<"unspreadLight modified "<<diff<<std::endl;
843 //TimeTaker timer("spreadLight");
844 spreadLight(bank, light_sources, modified_blocks);
849 u32 diff = modified_blocks.size() - count_was;
850 count_was = modified_blocks.size();
851 infostream<<"spreadLight modified "<<diff<<std::endl;
857 //MapVoxelManipulator vmanip(this);
859 // Make a manual voxel manipulator and load all the blocks
860 // that touch the requested blocks
861 ManualMapVoxelManipulator vmanip(this);
864 //TimeTaker timer("initialEmerge");
866 core::map<v3s16, MapBlock*>::Iterator i;
867 i = blocks_to_update.getIterator();
868 for(; i.atEnd() == false; i++)
870 MapBlock *block = i.getNode()->getValue();
871 v3s16 p = block->getPos();
873 // Add all surrounding blocks
874 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
877 Add all surrounding blocks that have up-to-date lighting
878 NOTE: This doesn't quite do the job (not everything
879 appropriate is lighted)
881 /*for(s16 z=-1; z<=1; z++)
882 for(s16 y=-1; y<=1; y++)
883 for(s16 x=-1; x<=1; x++)
885 v3s16 p2 = p + v3s16(x,y,z);
886 MapBlock *block = getBlockNoCreateNoEx(p2);
891 if(block->getLightingExpired())
893 vmanip.initialEmerge(p2, p2);
896 // Lighting of block will be updated completely
897 block->setLightingExpired(false);
902 //TimeTaker timer("unSpreadLight");
903 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
906 //TimeTaker timer("spreadLight");
907 vmanip.spreadLight(bank, light_sources, nodemgr);
910 //TimeTaker timer("blitBack");
911 vmanip.blitBack(modified_blocks);
913 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
918 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
921 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
922 std::map<v3s16, MapBlock*> & modified_blocks)
924 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
925 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
928 Update information about whether day and night light differ
930 for(std::map<v3s16, MapBlock*>::iterator
931 i = modified_blocks.begin();
932 i != modified_blocks.end(); ++i)
934 MapBlock *block = i->second;
935 block->expireDayNightDiff();
941 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
942 std::map<v3s16, MapBlock*> &modified_blocks,
943 bool remove_metadata)
945 INodeDefManager *ndef = m_gamedef->ndef();
948 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
952 From this node to nodes underneath:
953 If lighting is sunlight (1.0), unlight neighbours and
958 v3s16 toppos = p + v3s16(0,1,0);
959 //v3s16 bottompos = p + v3s16(0,-1,0);
961 bool node_under_sunlight = true;
962 std::set<v3s16> light_sources;
965 Collect old node for rollback
967 RollbackNode rollback_oldnode(this, p, m_gamedef);
970 If there is a node at top and it doesn't have sunlight,
971 there has not been any sunlight going down.
973 Otherwise there probably is.
976 bool is_valid_position;
977 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
979 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
980 node_under_sunlight = false;
983 Remove all light that has come out of this node
986 enum LightBank banks[] =
991 for(s32 i=0; i<2; i++)
993 enum LightBank bank = banks[i];
995 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
997 // Add the block of the added node to modified_blocks
998 v3s16 blockpos = getNodeBlockPos(p);
999 MapBlock * block = getBlockNoCreate(blockpos);
1000 assert(block != NULL);
1001 modified_blocks[blockpos] = block;
1003 assert(isValidPosition(p));
1005 // Unlight neighbours of node.
1006 // This means setting light of all consequent dimmer nodes
1008 // This also collects the nodes at the border which will spread
1009 // light again into this.
1010 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1012 n.setLight(bank, 0, ndef);
1016 If node lets sunlight through and is under sunlight, it has
1019 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1021 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1025 Remove node metadata
1027 if (remove_metadata) {
1028 removeNodeMetadata(p);
1032 Set the node on the map
1038 If node is under sunlight and doesn't let sunlight through,
1039 take all sunlighted nodes under it and clear light from them
1040 and from where the light has been spread.
1041 TODO: This could be optimized by mass-unlighting instead
1044 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1048 //m_dout<<DTIME<<"y="<<y<<std::endl;
1049 v3s16 n2pos(p.X, y, p.Z);
1053 n2 = getNodeNoEx(n2pos, &is_valid_position);
1054 if (!is_valid_position)
1057 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1059 unLightNeighbors(LIGHTBANK_DAY,
1060 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1061 light_sources, modified_blocks);
1062 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1070 for(s32 i=0; i<2; i++)
1072 enum LightBank bank = banks[i];
1075 Spread light from all nodes that might be capable of doing so
1077 spreadLight(bank, light_sources, modified_blocks);
1081 Update information about whether day and night light differ
1083 for(std::map<v3s16, MapBlock*>::iterator
1084 i = modified_blocks.begin();
1085 i != modified_blocks.end(); ++i)
1087 i->second->expireDayNightDiff();
1093 if(m_gamedef->rollback())
1095 RollbackNode rollback_newnode(this, p, m_gamedef);
1096 RollbackAction action;
1097 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1098 m_gamedef->rollback()->reportAction(action);
1102 Add neighboring liquid nodes and the node itself if it is
1103 liquid (=water node was added) to transform queue.
1106 v3s16(0,0,0), // self
1107 v3s16(0,0,1), // back
1108 v3s16(0,1,0), // top
1109 v3s16(1,0,0), // right
1110 v3s16(0,0,-1), // front
1111 v3s16(0,-1,0), // bottom
1112 v3s16(-1,0,0), // left
1114 for(u16 i=0; i<7; i++)
1116 v3s16 p2 = p + dirs[i];
1118 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1119 if(is_valid_position
1120 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1122 m_transforming_liquid.push_back(p2);
1129 void Map::removeNodeAndUpdate(v3s16 p,
1130 std::map<v3s16, MapBlock*> &modified_blocks)
1132 INodeDefManager *ndef = m_gamedef->ndef();
1134 /*PrintInfo(m_dout);
1135 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1136 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1138 bool node_under_sunlight = true;
1140 v3s16 toppos = p + v3s16(0,1,0);
1142 // Node will be replaced with this
1143 content_t replace_material = CONTENT_AIR;
1146 Collect old node for rollback
1148 RollbackNode rollback_oldnode(this, p, m_gamedef);
1151 If there is a node at top and it doesn't have sunlight,
1152 there will be no sunlight going down.
1154 bool is_valid_position;
1155 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1157 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1158 node_under_sunlight = false;
1160 std::set<v3s16> light_sources;
1162 enum LightBank banks[] =
1167 for(s32 i=0; i<2; i++)
1169 enum LightBank bank = banks[i];
1172 Unlight neighbors (in case the node is a light source)
1174 unLightNeighbors(bank, p,
1175 getNodeNoEx(p).getLight(bank, ndef),
1176 light_sources, modified_blocks);
1180 Remove node metadata
1183 removeNodeMetadata(p);
1187 This also clears the lighting.
1190 MapNode n(replace_material);
1193 for(s32 i=0; i<2; i++)
1195 enum LightBank bank = banks[i];
1198 Recalculate lighting
1200 spreadLight(bank, light_sources, modified_blocks);
1203 // Add the block of the removed node to modified_blocks
1204 v3s16 blockpos = getNodeBlockPos(p);
1205 MapBlock * block = getBlockNoCreate(blockpos);
1206 assert(block != NULL);
1207 modified_blocks[blockpos] = block;
1210 If the removed node was under sunlight, propagate the
1211 sunlight down from it and then light all neighbors
1212 of the propagated blocks.
1214 if(node_under_sunlight)
1216 s16 ybottom = propagateSunlight(p, modified_blocks);
1217 /*m_dout<<DTIME<<"Node was under sunlight. "
1218 "Propagating sunlight";
1219 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1221 for(; y >= ybottom; y--)
1223 v3s16 p2(p.X, y, p.Z);
1224 /*m_dout<<DTIME<<"lighting neighbors of node ("
1225 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1227 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1232 // Set the lighting of this node to 0
1233 // TODO: Is this needed? Lighting is cleared up there already.
1234 MapNode n = getNodeNoEx(p, &is_valid_position);
1235 if (is_valid_position) {
1236 n.setLight(LIGHTBANK_DAY, 0, ndef);
1243 for(s32 i=0; i<2; i++)
1245 enum LightBank bank = banks[i];
1247 // Get the brightest neighbour node and propagate light from it
1248 v3s16 n2p = getBrightestNeighbour(bank, p);
1250 //MapNode n2 = getNode(n2p);
1251 lightNeighbors(bank, n2p, modified_blocks);
1253 catch(InvalidPositionException &e)
1259 Update information about whether day and night light differ
1261 for(std::map<v3s16, MapBlock*>::iterator
1262 i = modified_blocks.begin();
1263 i != modified_blocks.end(); ++i)
1265 i->second->expireDayNightDiff();
1271 if(m_gamedef->rollback())
1273 RollbackNode rollback_newnode(this, p, m_gamedef);
1274 RollbackAction action;
1275 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1276 m_gamedef->rollback()->reportAction(action);
1280 Add neighboring liquid nodes and this node to transform queue.
1281 (it's vital for the node itself to get updated last.)
1284 v3s16(0,0,1), // back
1285 v3s16(0,1,0), // top
1286 v3s16(1,0,0), // right
1287 v3s16(0,0,-1), // front
1288 v3s16(0,-1,0), // bottom
1289 v3s16(-1,0,0), // left
1290 v3s16(0,0,0), // self
1292 for(u16 i=0; i<7; i++)
1294 v3s16 p2 = p + dirs[i];
1296 bool is_position_valid;
1297 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1298 if (is_position_valid
1299 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1301 m_transforming_liquid.push_back(p2);
1306 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1309 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1313 bool succeeded = true;
1315 std::map<v3s16, MapBlock*> modified_blocks;
1316 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1318 // Copy modified_blocks to event
1319 for(std::map<v3s16, MapBlock*>::iterator
1320 i = modified_blocks.begin();
1321 i != modified_blocks.end(); ++i)
1323 event.modified_blocks.insert(i->first);
1326 catch(InvalidPositionException &e){
1330 dispatchEvent(&event);
1335 bool Map::removeNodeWithEvent(v3s16 p)
1338 event.type = MEET_REMOVENODE;
1341 bool succeeded = true;
1343 std::map<v3s16, MapBlock*> modified_blocks;
1344 removeNodeAndUpdate(p, modified_blocks);
1346 // Copy modified_blocks to event
1347 for(std::map<v3s16, MapBlock*>::iterator
1348 i = modified_blocks.begin();
1349 i != modified_blocks.end(); ++i)
1351 event.modified_blocks.insert(i->first);
1354 catch(InvalidPositionException &e){
1358 dispatchEvent(&event);
1363 bool Map::getDayNightDiff(v3s16 blockpos)
1366 v3s16 p = blockpos + v3s16(0,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->getDayNightDiff())
1371 catch(InvalidPositionException &e){}
1374 v3s16 p = blockpos + v3s16(-1,0,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->getDayNightDiff())
1379 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(0,-1,0);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->getDayNightDiff())
1386 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(0,0,-1);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->getDayNightDiff())
1393 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(1,0,0);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1403 v3s16 p = blockpos + v3s16(0,1,0);
1404 MapBlock *b = getBlockNoCreate(p);
1405 if(b->getDayNightDiff())
1408 catch(InvalidPositionException &e){}
1410 v3s16 p = blockpos + v3s16(0,0,1);
1411 MapBlock *b = getBlockNoCreate(p);
1412 if(b->getDayNightDiff())
1415 catch(InvalidPositionException &e){}
1421 Updates usage timers
1423 void Map::timerUpdate(float dtime, float unload_timeout,
1424 std::list<v3s16> *unloaded_blocks)
1426 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1428 // Profile modified reasons
1429 Profiler modprofiler;
1431 std::list<v2s16> sector_deletion_queue;
1432 u32 deleted_blocks_count = 0;
1433 u32 saved_blocks_count = 0;
1434 u32 block_count_all = 0;
1437 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1438 si != m_sectors.end(); ++si)
1440 MapSector *sector = si->second;
1442 bool all_blocks_deleted = true;
1444 std::list<MapBlock*> blocks;
1445 sector->getBlocks(blocks);
1447 for(std::list<MapBlock*>::iterator i = blocks.begin();
1448 i != blocks.end(); ++i)
1450 MapBlock *block = (*i);
1452 block->incrementUsageTimer(dtime);
1454 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1456 v3s16 p = block->getPos();
1459 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1461 modprofiler.add(block->getModifiedReason(), 1);
1462 if (!saveBlock(block))
1464 saved_blocks_count++;
1467 // Delete from memory
1468 sector->deleteBlock(block);
1471 unloaded_blocks->push_back(p);
1473 deleted_blocks_count++;
1477 all_blocks_deleted = false;
1482 if(all_blocks_deleted)
1484 sector_deletion_queue.push_back(si->first);
1489 // Finally delete the empty sectors
1490 deleteSectors(sector_deletion_queue);
1492 if(deleted_blocks_count != 0)
1494 PrintInfo(infostream); // ServerMap/ClientMap:
1495 infostream<<"Unloaded "<<deleted_blocks_count
1496 <<" blocks from memory";
1497 if(save_before_unloading)
1498 infostream<<", of which "<<saved_blocks_count<<" were written";
1499 infostream<<", "<<block_count_all<<" blocks in memory";
1500 infostream<<"."<<std::endl;
1501 if(saved_blocks_count != 0){
1502 PrintInfo(infostream); // ServerMap/ClientMap:
1503 infostream<<"Blocks modified by: "<<std::endl;
1504 modprofiler.print(infostream);
1509 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1511 timerUpdate(0.0, -1.0, unloaded_blocks);
1514 void Map::deleteSectors(std::list<v2s16> &list)
1516 for(std::list<v2s16>::iterator j = list.begin();
1517 j != list.end(); ++j)
1519 MapSector *sector = m_sectors[*j];
1520 // If sector is in sector cache, remove it from there
1521 if(m_sector_cache == sector)
1522 m_sector_cache = NULL;
1523 // Remove from map and delete
1524 m_sectors.erase(*j);
1530 void Map::unloadUnusedData(float timeout,
1531 core::list<v3s16> *deleted_blocks)
1533 core::list<v2s16> sector_deletion_queue;
1534 u32 deleted_blocks_count = 0;
1535 u32 saved_blocks_count = 0;
1537 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1538 for(; si.atEnd() == false; si++)
1540 MapSector *sector = si.getNode()->getValue();
1542 bool all_blocks_deleted = true;
1544 core::list<MapBlock*> blocks;
1545 sector->getBlocks(blocks);
1546 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1547 i != blocks.end(); i++)
1549 MapBlock *block = (*i);
1551 if(block->getUsageTimer() > timeout)
1554 if(block->getModified() != MOD_STATE_CLEAN)
1557 saved_blocks_count++;
1559 // Delete from memory
1560 sector->deleteBlock(block);
1561 deleted_blocks_count++;
1565 all_blocks_deleted = false;
1569 if(all_blocks_deleted)
1571 sector_deletion_queue.push_back(si.getNode()->getKey());
1575 deleteSectors(sector_deletion_queue);
1577 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1578 <<", of which "<<saved_blocks_count<<" were wr."
1581 //return sector_deletion_queue.getSize();
1582 //return deleted_blocks_count;
1586 void Map::PrintInfo(std::ostream &out)
1591 #define WATER_DROP_BOOST 4
1595 NEIGHBOR_SAME_LEVEL,
1598 struct NodeNeighbor {
1602 bool l; //can liquid
1608 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1615 void Map::transforming_liquid_add(v3s16 p) {
1616 m_transforming_liquid.push_back(p);
1619 s32 Map::transforming_liquid_size() {
1620 return m_transforming_liquid.size();
1623 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1626 INodeDefManager *nodemgr = m_gamedef->ndef();
1628 DSTACK(__FUNCTION_NAME);
1629 //TimeTaker timer("transformLiquids()");
1632 u32 initial_size = m_transforming_liquid.size();
1634 /*if(initial_size != 0)
1635 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1637 // list of nodes that due to viscosity have not reached their max level height
1638 std::deque<v3s16> must_reflow;
1640 // List of MapBlocks that will require a lighting update (due to lava)
1641 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1643 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1644 u32 loop_max = liquid_loop_max;
1648 /* If liquid_loop_max is not keeping up with the queue size increase
1649 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1651 if (m_transforming_liquid.size() > loop_max * 2) {
1653 float server_step = g_settings->getFloat("dedicated_server_step");
1654 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1655 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1657 m_transforming_liquid_loop_count_multiplier = 1.0;
1660 loop_max *= m_transforming_liquid_loop_count_multiplier;
1663 while(m_transforming_liquid.size() != 0)
1665 // This should be done here so that it is done when continue is used
1666 if(loopcount >= initial_size || loopcount >= loop_max)
1671 Get a queued transforming liquid node
1673 v3s16 p0 = m_transforming_liquid.front();
1674 m_transforming_liquid.pop_front();
1676 MapNode n0 = getNodeNoEx(p0);
1679 Collect information about current node
1681 s8 liquid_level = -1;
1682 content_t liquid_kind = CONTENT_IGNORE;
1683 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1684 switch (liquid_type) {
1686 liquid_level = LIQUID_LEVEL_SOURCE;
1687 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1689 case LIQUID_FLOWING:
1690 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1691 liquid_kind = n0.getContent();
1694 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1695 // continue with the next node.
1696 if (n0.getContent() != CONTENT_AIR)
1698 liquid_kind = CONTENT_AIR;
1703 Collect information about the environment
1705 const v3s16 *dirs = g_6dirs;
1706 NodeNeighbor sources[6]; // surrounding sources
1707 int num_sources = 0;
1708 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1710 NodeNeighbor airs[6]; // surrounding air
1712 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1713 int num_neutrals = 0;
1714 bool flowing_down = false;
1715 for (u16 i = 0; i < 6; i++) {
1716 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1719 nt = NEIGHBOR_UPPER;
1722 nt = NEIGHBOR_LOWER;
1725 v3s16 npos = p0 + dirs[i];
1726 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1727 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1729 if (nb.n.getContent() == CONTENT_AIR) {
1730 airs[num_airs++] = nb;
1731 // if the current node is a water source the neighbor
1732 // should be enqueded for transformation regardless of whether the
1733 // current node changes or not.
1734 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1735 m_transforming_liquid.push_back(npos);
1736 // if the current node happens to be a flowing node, it will start to flow down here.
1737 if (nb.t == NEIGHBOR_LOWER) {
1738 flowing_down = true;
1741 neutrals[num_neutrals++] = nb;
1745 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1746 if (liquid_kind == CONTENT_AIR)
1747 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1748 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1749 neutrals[num_neutrals++] = nb;
1751 // Do not count bottom source, it will screw things up
1753 sources[num_sources++] = nb;
1756 case LIQUID_FLOWING:
1757 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1758 if (liquid_kind == CONTENT_AIR)
1759 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1760 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1761 neutrals[num_neutrals++] = nb;
1763 flows[num_flows++] = nb;
1764 if (nb.t == NEIGHBOR_LOWER)
1765 flowing_down = true;
1772 decide on the type (and possibly level) of the current node
1774 content_t new_node_content;
1775 s8 new_node_level = -1;
1776 s8 max_node_level = -1;
1778 u8 range = nodemgr->get(liquid_kind).liquid_range;
1779 if (range > LIQUID_LEVEL_MAX+1)
1780 range = LIQUID_LEVEL_MAX+1;
1782 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1783 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1784 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1785 // it's perfectly safe to use liquid_kind here to determine the new node content.
1786 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1787 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1788 // liquid_kind is set properly, see above
1789 new_node_content = liquid_kind;
1790 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1791 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1792 new_node_content = CONTENT_AIR;
1794 // no surrounding sources, so get the maximum level that can flow into this node
1795 for (u16 i = 0; i < num_flows; i++) {
1796 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1797 switch (flows[i].t) {
1798 case NEIGHBOR_UPPER:
1799 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1800 max_node_level = LIQUID_LEVEL_MAX;
1801 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1802 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1803 } else if (nb_liquid_level > max_node_level)
1804 max_node_level = nb_liquid_level;
1806 case NEIGHBOR_LOWER:
1808 case NEIGHBOR_SAME_LEVEL:
1809 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1810 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1811 max_node_level = nb_liquid_level - 1;
1817 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1818 if (viscosity > 1 && max_node_level != liquid_level) {
1819 // amount to gain, limited by viscosity
1820 // must be at least 1 in absolute value
1821 s8 level_inc = max_node_level - liquid_level;
1822 if (level_inc < -viscosity || level_inc > viscosity)
1823 new_node_level = liquid_level + level_inc/viscosity;
1824 else if (level_inc < 0)
1825 new_node_level = liquid_level - 1;
1826 else if (level_inc > 0)
1827 new_node_level = liquid_level + 1;
1828 if (new_node_level != max_node_level)
1829 must_reflow.push_back(p0);
1831 new_node_level = max_node_level;
1833 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1834 new_node_content = liquid_kind;
1836 new_node_content = CONTENT_AIR;
1841 check if anything has changed. if not, just continue with the next node.
1843 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1844 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1845 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1851 update the current node
1854 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1855 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1856 // set level to last 3 bits, flowing down bit to 4th bit
1857 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1859 // set the liquid level and flow bit to 0
1860 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1862 n0.setContent(new_node_content);
1864 // Find out whether there is a suspect for this action
1865 std::string suspect;
1866 if(m_gamedef->rollback()){
1867 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1870 if(!suspect.empty()){
1872 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1873 // Get old node for rollback
1874 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1878 RollbackNode rollback_newnode(this, p0, m_gamedef);
1879 RollbackAction action;
1880 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1881 m_gamedef->rollback()->reportAction(action);
1887 v3s16 blockpos = getNodeBlockPos(p0);
1888 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1890 modified_blocks[blockpos] = block;
1891 // If new or old node emits light, MapBlock requires lighting update
1892 if(nodemgr->get(n0).light_source != 0 ||
1893 nodemgr->get(n00).light_source != 0)
1894 lighting_modified_blocks[block->getPos()] = block;
1898 enqueue neighbors for update if neccessary
1900 switch (nodemgr->get(n0.getContent()).liquid_type) {
1902 case LIQUID_FLOWING:
1903 // make sure source flows into all neighboring nodes
1904 for (u16 i = 0; i < num_flows; i++)
1905 if (flows[i].t != NEIGHBOR_UPPER)
1906 m_transforming_liquid.push_back(flows[i].p);
1907 for (u16 i = 0; i < num_airs; i++)
1908 if (airs[i].t != NEIGHBOR_UPPER)
1909 m_transforming_liquid.push_back(airs[i].p);
1912 // this flow has turned to air; neighboring flows might need to do the same
1913 for (u16 i = 0; i < num_flows; i++)
1914 m_transforming_liquid.push_back(flows[i].p);
1918 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1920 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1921 m_transforming_liquid.push_back(*iter);
1923 updateLighting(lighting_modified_blocks, modified_blocks);
1926 /* ----------------------------------------------------------------------
1927 * Manage the queue so that it does not grow indefinately
1929 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1931 if (time_until_purge == 0)
1932 return; // Feature disabled
1934 time_until_purge *= 1000; // seconds -> milliseconds
1936 u32 curr_time = getTime(PRECISION_MILLI);
1937 u32 prev_unprocessed = m_unprocessed_count;
1938 m_unprocessed_count = m_transforming_liquid.size();
1940 // if unprocessed block count is decreasing or stable
1941 if (m_unprocessed_count <= prev_unprocessed) {
1942 m_queue_size_timer_started = false;
1944 if (!m_queue_size_timer_started)
1945 m_inc_trending_up_start_time = curr_time;
1946 m_queue_size_timer_started = true;
1949 // Account for curr_time overflowing
1950 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1951 m_queue_size_timer_started = false;
1953 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1954 * and the number of unprocessed blocks is still > liquid_loop_max then we
1955 * cannot keep up; dump the oldest blocks from the queue so that the queue
1956 * has liquid_loop_max items in it
1958 if (m_queue_size_timer_started
1959 && curr_time - m_inc_trending_up_start_time > time_until_purge
1960 && m_unprocessed_count > liquid_loop_max) {
1962 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1964 infostream << "transformLiquids(): DUMPING " << dump_qty
1965 << " blocks from the queue" << std::endl;
1968 m_transforming_liquid.pop_front();
1970 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1971 m_unprocessed_count = m_transforming_liquid.size();
1975 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1977 v3s16 blockpos = getNodeBlockPos(p);
1978 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1979 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1981 infostream<<"Map::getNodeMetadata(): Need to emerge "
1982 <<PP(blockpos)<<std::endl;
1983 block = emergeBlock(blockpos, false);
1986 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1990 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1994 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1996 v3s16 blockpos = getNodeBlockPos(p);
1997 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1998 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2000 infostream<<"Map::setNodeMetadata(): Need to emerge "
2001 <<PP(blockpos)<<std::endl;
2002 block = emergeBlock(blockpos, false);
2005 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2009 block->m_node_metadata.set(p_rel, meta);
2013 void Map::removeNodeMetadata(v3s16 p)
2015 v3s16 blockpos = getNodeBlockPos(p);
2016 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2017 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2020 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2024 block->m_node_metadata.remove(p_rel);
2027 NodeTimer Map::getNodeTimer(v3s16 p)
2029 v3s16 blockpos = getNodeBlockPos(p);
2030 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2031 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2033 infostream<<"Map::getNodeTimer(): Need to emerge "
2034 <<PP(blockpos)<<std::endl;
2035 block = emergeBlock(blockpos, false);
2038 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2042 NodeTimer t = block->m_node_timers.get(p_rel);
2046 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2048 v3s16 blockpos = getNodeBlockPos(p);
2049 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2050 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2052 infostream<<"Map::setNodeTimer(): Need to emerge "
2053 <<PP(blockpos)<<std::endl;
2054 block = emergeBlock(blockpos, false);
2057 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2061 block->m_node_timers.set(p_rel, t);
2064 void Map::removeNodeTimer(v3s16 p)
2066 v3s16 blockpos = getNodeBlockPos(p);
2067 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2068 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2071 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2075 block->m_node_timers.remove(p_rel);
2081 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2082 Map(dout_server, gamedef),
2084 m_map_metadata_changed(true)
2086 verbosestream<<__FUNCTION_NAME<<std::endl;
2089 Try to load map; if not found, create a new one.
2092 // Determine which database backend to use
2093 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2095 bool succeeded = conf.readConfigFile(conf_path.c_str());
2096 if (!succeeded || !conf.exists("backend")) {
2097 // fall back to sqlite3
2098 dbase = new Database_SQLite3(this, savedir);
2099 conf.set("backend", "sqlite3");
2101 std::string backend = conf.get("backend");
2102 if (backend == "dummy")
2103 dbase = new Database_Dummy(this);
2104 else if (backend == "sqlite3")
2105 dbase = new Database_SQLite3(this, savedir);
2107 else if (backend == "leveldb")
2108 dbase = new Database_LevelDB(this, savedir);
2111 else if (backend == "redis")
2112 dbase = new Database_Redis(this, savedir);
2115 throw BaseException("Unknown map backend");
2118 m_savedir = savedir;
2119 m_map_saving_enabled = false;
2123 // If directory exists, check contents and load if possible
2124 if(fs::PathExists(m_savedir))
2126 // If directory is empty, it is safe to save into it.
2127 if(fs::GetDirListing(m_savedir).size() == 0)
2129 infostream<<"ServerMap: Empty save directory is valid."
2131 m_map_saving_enabled = true;
2136 // Load map metadata (seed, chunksize)
2139 catch(SettingNotFoundException &e){
2140 infostream<<"ServerMap: Some metadata not found."
2141 <<" Using default settings."<<std::endl;
2143 catch(FileNotGoodException &e){
2144 infostream<<"WARNING: Could not load map metadata"
2145 //<<" Disabling chunk-based generator."
2150 infostream<<"ServerMap: Successfully loaded map "
2151 <<"metadata from "<<savedir
2152 <<", assuming valid save directory."
2153 <<" seed="<< m_emerge->params.seed <<"."
2156 m_map_saving_enabled = true;
2157 // Map loaded, not creating new one
2161 // If directory doesn't exist, it is safe to save to it
2163 m_map_saving_enabled = true;
2166 catch(std::exception &e)
2168 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2169 <<", exception: "<<e.what()<<std::endl;
2170 infostream<<"Please remove the map or fix it."<<std::endl;
2171 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2174 infostream<<"Initializing new map."<<std::endl;
2176 // Create zero sector
2177 emergeSector(v2s16(0,0));
2179 // Initially write whole map
2180 save(MOD_STATE_CLEAN);
2183 ServerMap::~ServerMap()
2185 verbosestream<<__FUNCTION_NAME<<std::endl;
2189 if(m_map_saving_enabled)
2191 // Save only changed parts
2192 save(MOD_STATE_WRITE_AT_UNLOAD);
2193 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2197 infostream<<"ServerMap: Map not saved"<<std::endl;
2200 catch(std::exception &e)
2202 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2203 <<", exception: "<<e.what()<<std::endl;
2207 Close database if it was opened
2215 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2216 for(; i.atEnd() == false; i++)
2218 MapChunk *chunk = i.getNode()->getValue();
2224 u64 ServerMap::getSeed()
2226 return m_emerge->params.seed;
2229 s16 ServerMap::getWaterLevel()
2231 return m_emerge->params.water_level;
2234 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2236 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2237 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2239 s16 chunksize = m_emerge->params.chunksize;
2240 s16 coffset = -chunksize / 2;
2241 v3s16 chunk_offset(coffset, coffset, coffset);
2242 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2243 v3s16 blockpos_min = blockpos_div * chunksize;
2244 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2245 blockpos_min += chunk_offset;
2246 blockpos_max += chunk_offset;
2248 v3s16 extra_borders(1,1,1);
2250 // Do nothing if not inside limits (+-1 because of neighbors)
2251 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2252 blockpos_over_limit(blockpos_max + extra_borders))
2255 data->seed = m_emerge->params.seed;
2256 data->blockpos_min = blockpos_min;
2257 data->blockpos_max = blockpos_max;
2258 data->blockpos_requested = blockpos;
2259 data->nodedef = m_gamedef->ndef();
2262 Create the whole area of this and the neighboring blocks
2265 //TimeTaker timer("initBlockMake() create area");
2267 for(s16 x=blockpos_min.X-extra_borders.X;
2268 x<=blockpos_max.X+extra_borders.X; x++)
2269 for(s16 z=blockpos_min.Z-extra_borders.Z;
2270 z<=blockpos_max.Z+extra_borders.Z; z++)
2272 v2s16 sectorpos(x, z);
2273 // Sector metadata is loaded from disk if not already loaded.
2274 ServerMapSector *sector = createSector(sectorpos);
2278 for(s16 y=blockpos_min.Y-extra_borders.Y;
2279 y<=blockpos_max.Y+extra_borders.Y; y++)
2282 //MapBlock *block = createBlock(p);
2283 // 1) get from memory, 2) load from disk
2284 MapBlock *block = emergeBlock(p, false);
2285 // 3) create a blank one
2288 block = createBlock(p);
2291 Block gets sunlight if this is true.
2293 Refer to the map generator heuristics.
2295 bool ug = m_emerge->isBlockUnderground(p);
2296 block->setIsUnderground(ug);
2299 // Lighting will not be valid after make_chunk is called
2300 block->setLightingExpired(true);
2301 // Lighting will be calculated
2302 //block->setLightingExpired(false);
2308 Now we have a big empty area.
2310 Make a ManualMapVoxelManipulator that contains this and the
2314 // The area that contains this block and it's neighbors
2315 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2316 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2318 data->vmanip = new MMVManip(this);
2319 //data->vmanip->setMap(this);
2323 //TimeTaker timer("initBlockMake() initialEmerge");
2324 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2327 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2328 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2329 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2330 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2331 core::map<v3s16, u8>::Node *n;
2332 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2335 u8 flags = n->getValue();
2336 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2342 // Data is ready now.
2346 void ServerMap::finishBlockMake(BlockMakeData *data,
2347 std::map<v3s16, MapBlock*> &changed_blocks)
2349 v3s16 blockpos_min = data->blockpos_min;
2350 v3s16 blockpos_max = data->blockpos_max;
2351 v3s16 blockpos_requested = data->blockpos_requested;
2352 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2353 <<blockpos_requested.Y<<","
2354 <<blockpos_requested.Z<<")"<<std::endl;*/
2356 v3s16 extra_borders(1,1,1);
2358 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2360 /*infostream<<"Resulting vmanip:"<<std::endl;
2361 data->vmanip.print(infostream);*/
2363 // Make sure affected blocks are loaded
2364 for(s16 x=blockpos_min.X-extra_borders.X;
2365 x<=blockpos_max.X+extra_borders.X; x++)
2366 for(s16 z=blockpos_min.Z-extra_borders.Z;
2367 z<=blockpos_max.Z+extra_borders.Z; z++)
2368 for(s16 y=blockpos_min.Y-extra_borders.Y;
2369 y<=blockpos_max.Y+extra_borders.Y; y++)
2372 // Load from disk if not already in memory
2373 emergeBlock(p, false);
2377 Blit generated stuff to map
2378 NOTE: blitBackAll adds nearly everything to changed_blocks
2382 //TimeTaker timer("finishBlockMake() blitBackAll");
2383 data->vmanip->blitBackAll(&changed_blocks);
2386 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2389 Copy transforming liquid information
2391 while(data->transforming_liquid.size() > 0)
2393 m_transforming_liquid.push_back(data->transforming_liquid.front());
2394 data->transforming_liquid.pop_front();
2398 Do stuff in central blocks
2406 TimeTaker t("finishBlockMake lighting update");
2408 core::map<v3s16, MapBlock*> lighting_update_blocks;
2411 for(s16 x=blockpos_min.X-extra_borders.X;
2412 x<=blockpos_max.X+extra_borders.X; x++)
2413 for(s16 z=blockpos_min.Z-extra_borders.Z;
2414 z<=blockpos_max.Z+extra_borders.Z; z++)
2415 for(s16 y=blockpos_min.Y-extra_borders.Y;
2416 y<=blockpos_max.Y+extra_borders.Y; y++)
2419 MapBlock *block = getBlockNoCreateNoEx(p);
2421 lighting_update_blocks.insert(block->getPos(), block);
2424 updateLighting(lighting_update_blocks, changed_blocks);
2428 Set lighting to non-expired state in all of them.
2429 This is cheating, but it is not fast enough if all of them
2430 would actually be updated.
2432 for(s16 x=blockpos_min.X-extra_borders.X;
2433 x<=blockpos_max.X+extra_borders.X; x++)
2434 for(s16 z=blockpos_min.Z-extra_borders.Z;
2435 z<=blockpos_max.Z+extra_borders.Z; z++)
2436 for(s16 y=blockpos_min.Y-extra_borders.Y;
2437 y<=blockpos_max.Y+extra_borders.Y; y++)
2440 MapBlock * block = getBlockNoCreateNoEx(p);
2442 block->setLightingExpired(false);
2446 if(enable_mapgen_debug_info == false)
2447 t.stop(true); // Hide output
2452 Go through changed blocks
2454 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2455 i != changed_blocks.end(); ++i)
2457 MapBlock *block = i->second;
2461 Update day/night difference cache of the MapBlocks
2463 block->expireDayNightDiff();
2465 Set block as modified
2467 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2468 "finishBlockMake expireDayNightDiff");
2472 Set central blocks as generated
2474 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2475 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2476 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2479 MapBlock *block = getBlockNoCreateNoEx(p);
2482 block->setGenerated(true);
2486 Save changed parts of map
2487 NOTE: Will be saved later.
2489 //save(MOD_STATE_WRITE_AT_UNLOAD);
2491 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2492 <<","<<blockpos_requested.Y<<","
2493 <<blockpos_requested.Z<<")"<<std::endl;*/
2497 if(enable_mapgen_debug_info)
2500 Analyze resulting blocks
2502 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2503 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2504 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2505 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2506 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2507 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2509 v3s16 p = v3s16(x,y,z);
2510 MapBlock *block = getBlockNoCreateNoEx(p);
2512 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2513 infostream<<"Generated "<<spos<<": "
2514 <<analyze_block(block)<<std::endl;
2519 getBlockNoCreateNoEx(blockpos_requested);
2522 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2524 DSTACKF("%s: p2d=(%d,%d)",
2529 Check if it exists already in memory
2531 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2536 Try to load it from disk (with blocks)
2538 //if(loadSectorFull(p2d) == true)
2541 Try to load metadata from disk
2544 if(loadSectorMeta(p2d) == true)
2546 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2549 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2550 throw InvalidPositionException("");
2556 Do not create over-limit
2558 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2559 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2560 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2561 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2562 throw InvalidPositionException("createSector(): pos. over limit");
2565 Generate blank sector
2568 sector = new ServerMapSector(this, p2d, m_gamedef);
2570 // Sector position on map in nodes
2571 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2576 m_sectors[p2d] = sector;
2583 This is a quick-hand function for calling makeBlock().
2585 MapBlock * ServerMap::generateBlock(
2587 std::map<v3s16, MapBlock*> &modified_blocks
2590 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2592 /*infostream<<"generateBlock(): "
2593 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2596 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2598 TimeTaker timer("generateBlock");
2600 //MapBlock *block = original_dummy;
2602 v2s16 p2d(p.X, p.Z);
2603 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2606 Do not generate over-limit
2608 if(blockpos_over_limit(p))
2610 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2611 throw InvalidPositionException("generateBlock(): pos. over limit");
2615 Create block make data
2618 initBlockMake(&data, p);
2624 TimeTaker t("mapgen::make_block()");
2625 mapgen->makeChunk(&data);
2626 //mapgen::make_block(&data);
2628 if(enable_mapgen_debug_info == false)
2629 t.stop(true); // Hide output
2633 Blit data back on map, update lighting, add mobs and whatever this does
2635 finishBlockMake(&data, modified_blocks);
2640 MapBlock *block = getBlockNoCreateNoEx(p);
2648 bool erroneus_content = false;
2649 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2650 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2651 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2654 MapNode n = block->getNode(p);
2655 if(n.getContent() == CONTENT_IGNORE)
2657 infostream<<"CONTENT_IGNORE at "
2658 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2660 erroneus_content = true;
2664 if(erroneus_content)
2673 Generate a completely empty block
2677 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2678 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2680 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2683 n.setContent(CONTENT_AIR);
2684 block->setNode(v3s16(x0,y0,z0), n);
2690 if(enable_mapgen_debug_info == false)
2691 timer.stop(true); // Hide output
2697 MapBlock * ServerMap::createBlock(v3s16 p)
2699 DSTACKF("%s: p=(%d,%d,%d)",
2700 __FUNCTION_NAME, p.X, p.Y, p.Z);
2703 Do not create over-limit
2705 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2706 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2707 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2708 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2709 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2710 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2711 throw InvalidPositionException("createBlock(): pos. over limit");
2713 v2s16 p2d(p.X, p.Z);
2716 This will create or load a sector if not found in memory.
2717 If block exists on disk, it will be loaded.
2719 NOTE: On old save formats, this will be slow, as it generates
2720 lighting on blocks for them.
2722 ServerMapSector *sector;
2724 sector = (ServerMapSector*)createSector(p2d);
2725 assert(sector->getId() == MAPSECTOR_SERVER);
2727 catch(InvalidPositionException &e)
2729 infostream<<"createBlock: createSector() failed"<<std::endl;
2733 NOTE: This should not be done, or at least the exception
2734 should not be passed on as std::exception, because it
2735 won't be catched at all.
2737 /*catch(std::exception &e)
2739 infostream<<"createBlock: createSector() failed: "
2740 <<e.what()<<std::endl;
2745 Try to get a block from the sector
2748 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2751 if(block->isDummy())
2756 block = sector->createBlankBlock(block_y);
2761 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2763 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2765 p.X, p.Y, p.Z, create_blank);
2768 MapBlock *block = getBlockNoCreateNoEx(p);
2769 if(block && block->isDummy() == false)
2774 MapBlock *block = loadBlock(p);
2780 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2781 MapBlock *block = sector->createBlankBlock(p.Y);
2789 std::map<v3s16, MapBlock*> modified_blocks;
2790 MapBlock *block = generateBlock(p, modified_blocks);
2794 event.type = MEET_OTHER;
2797 // Copy modified_blocks to event
2798 for(std::map<v3s16, MapBlock*>::iterator
2799 i = modified_blocks.begin();
2800 i != modified_blocks.end(); ++i)
2802 event.modified_blocks.insert(i->first);
2806 dispatchEvent(&event);
2816 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2818 MapBlock *block = getBlockNoCreateNoEx(p3d);
2820 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2825 void ServerMap::prepareBlock(MapBlock *block) {
2828 // N.B. This requires no synchronization, since data will not be modified unless
2829 // the VoxelManipulator being updated belongs to the same thread.
2830 void ServerMap::updateVManip(v3s16 pos)
2832 Mapgen *mg = m_emerge->getCurrentMapgen();
2836 MMVManip *vm = mg->vm;
2840 if (!vm->m_area.contains(pos))
2843 s32 idx = vm->m_area.index(pos);
2844 vm->m_data[idx] = getNodeNoEx(pos);
2845 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2847 vm->m_is_dirty = true;
2850 s16 ServerMap::findGroundLevel(v2s16 p2d)
2854 Uh, just do something random...
2856 // Find existing map from top to down
2859 v3s16 p(p2d.X, max, p2d.Y);
2860 for(; p.Y>min; p.Y--)
2862 MapNode n = getNodeNoEx(p);
2863 if(n.getContent() != CONTENT_IGNORE)
2868 // If this node is not air, go to plan b
2869 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2871 // Search existing walkable and return it
2872 for(; p.Y>min; p.Y--)
2874 MapNode n = getNodeNoEx(p);
2875 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2884 Determine from map generator noise functions
2887 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2890 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2891 //return (s16)level;
2894 bool ServerMap::loadFromFolders() {
2895 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2900 void ServerMap::createDirs(std::string path)
2902 if(fs::CreateAllDirs(path) == false)
2904 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2905 <<"\""<<path<<"\""<<std::endl;
2906 throw BaseException("ServerMap failed to create directory");
2910 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2916 snprintf(cc, 9, "%.4x%.4x",
2917 (unsigned int)pos.X&0xffff,
2918 (unsigned int)pos.Y&0xffff);
2920 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2922 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2923 (unsigned int)pos.X&0xfff,
2924 (unsigned int)pos.Y&0xfff);
2926 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2933 v2s16 ServerMap::getSectorPos(std::string dirname)
2935 unsigned int x = 0, y = 0;
2937 std::string component;
2938 fs::RemoveLastPathComponent(dirname, &component, 1);
2939 if(component.size() == 8)
2942 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2944 else if(component.size() == 3)
2947 fs::RemoveLastPathComponent(dirname, &component, 2);
2948 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2949 // Sign-extend the 12 bit values up to 16 bits...
2950 if(x&0x800) x|=0xF000;
2951 if(y&0x800) y|=0xF000;
2958 v2s16 pos((s16)x, (s16)y);
2962 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2964 v2s16 p2d = getSectorPos(sectordir);
2966 if(blockfile.size() != 4){
2967 throw InvalidFilenameException("Invalid block filename");
2970 int r = sscanf(blockfile.c_str(), "%4x", &y);
2972 throw InvalidFilenameException("Invalid block filename");
2973 return v3s16(p2d.X, y, p2d.Y);
2976 std::string ServerMap::getBlockFilename(v3s16 p)
2979 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2983 void ServerMap::save(ModifiedState save_level)
2985 DSTACK(__FUNCTION_NAME);
2986 if(m_map_saving_enabled == false)
2988 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2992 if(save_level == MOD_STATE_CLEAN)
2993 infostream<<"ServerMap: Saving whole map, this can take time."
2996 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3001 // Profile modified reasons
3002 Profiler modprofiler;
3004 u32 sector_meta_count = 0;
3005 u32 block_count = 0;
3006 u32 block_count_all = 0; // Number of blocks in memory
3008 // Don't do anything with sqlite unless something is really saved
3009 bool save_started = false;
3011 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
3012 i != m_sectors.end(); ++i)
3014 ServerMapSector *sector = (ServerMapSector*)i->second;
3015 assert(sector->getId() == MAPSECTOR_SERVER);
3017 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3019 saveSectorMeta(sector);
3020 sector_meta_count++;
3022 std::list<MapBlock*> blocks;
3023 sector->getBlocks(blocks);
3025 for(std::list<MapBlock*>::iterator j = blocks.begin();
3026 j != blocks.end(); ++j)
3028 MapBlock *block = *j;
3032 if(block->getModified() >= (u32)save_level)
3037 save_started = true;
3040 modprofiler.add(block->getModifiedReason(), 1);
3045 /*infostream<<"ServerMap: Written block ("
3046 <<block->getPos().X<<","
3047 <<block->getPos().Y<<","
3048 <<block->getPos().Z<<")"
3057 Only print if something happened or saved whole map
3059 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3060 || block_count != 0)
3062 infostream<<"ServerMap: Written: "
3063 <<sector_meta_count<<" sector metadata files, "
3064 <<block_count<<" block files"
3065 <<", "<<block_count_all<<" blocks in memory."
3067 PrintInfo(infostream); // ServerMap/ClientMap:
3068 infostream<<"Blocks modified by: "<<std::endl;
3069 modprofiler.print(infostream);
3073 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3075 if(loadFromFolders()){
3076 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3077 <<"all blocks that are stored in flat files"<<std::endl;
3079 dbase->listAllLoadableBlocks(dst);
3082 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3084 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3085 si != m_sectors.end(); ++si)
3087 MapSector *sector = si->second;
3089 std::list<MapBlock*> blocks;
3090 sector->getBlocks(blocks);
3092 for(std::list<MapBlock*>::iterator i = blocks.begin();
3093 i != blocks.end(); ++i)
3095 MapBlock *block = (*i);
3096 v3s16 p = block->getPos();
3102 void ServerMap::saveMapMeta()
3104 DSTACK(__FUNCTION_NAME);
3106 /*infostream<<"ServerMap::saveMapMeta(): "
3110 createDirs(m_savedir);
3112 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3113 std::ostringstream ss(std::ios_base::binary);
3117 m_emerge->saveParamsToSettings(¶ms);
3118 params.writeLines(ss);
3120 ss<<"[end_of_params]\n";
3122 if(!fs::safeWriteToFile(fullpath, ss.str()))
3124 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3125 <<"could not write "<<fullpath<<std::endl;
3126 throw FileNotGoodException("Cannot save chunk metadata");
3129 m_map_metadata_changed = false;
3132 void ServerMap::loadMapMeta()
3134 DSTACK(__FUNCTION_NAME);
3137 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3139 if (fs::PathExists(fullpath)) {
3140 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3142 errorstream << "ServerMap::loadMapMeta(): "
3143 "could not open " << fullpath << std::endl;
3144 throw FileNotGoodException("Cannot open map metadata");
3147 if (!params.parseConfigLines(is, "[end_of_params]")) {
3148 throw SerializationError("ServerMap::loadMapMeta(): "
3149 "[end_of_params] not found!");
3153 m_emerge->loadParamsFromSettings(¶ms);
3155 verbosestream << "ServerMap::loadMapMeta(): seed="
3156 << m_emerge->params.seed << std::endl;
3159 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3161 DSTACK(__FUNCTION_NAME);
3162 // Format used for writing
3163 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3165 v2s16 pos = sector->getPos();
3166 std::string dir = getSectorDir(pos);
3169 std::string fullpath = dir + DIR_DELIM + "meta";
3170 std::ostringstream ss(std::ios_base::binary);
3172 sector->serialize(ss, version);
3174 if(!fs::safeWriteToFile(fullpath, ss.str()))
3175 throw FileNotGoodException("Cannot write sector metafile");
3177 sector->differs_from_disk = false;
3180 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3182 DSTACK(__FUNCTION_NAME);
3184 v2s16 p2d = getSectorPos(sectordir);
3186 ServerMapSector *sector = NULL;
3188 std::string fullpath = sectordir + DIR_DELIM + "meta";
3189 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3190 if(is.good() == false)
3192 // If the directory exists anyway, it probably is in some old
3193 // format. Just go ahead and create the sector.
3194 if(fs::PathExists(sectordir))
3196 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3197 <<fullpath<<" doesn't exist but directory does."
3198 <<" Continuing with a sector with no metadata."
3200 sector = new ServerMapSector(this, p2d, m_gamedef);
3201 m_sectors[p2d] = sector;
3205 throw FileNotGoodException("Cannot open sector metafile");
3210 sector = ServerMapSector::deSerialize
3211 (is, this, p2d, m_sectors, m_gamedef);
3213 saveSectorMeta(sector);
3216 sector->differs_from_disk = false;
3221 bool ServerMap::loadSectorMeta(v2s16 p2d)
3223 DSTACK(__FUNCTION_NAME);
3225 // The directory layout we're going to load from.
3226 // 1 - original sectors/xxxxzzzz/
3227 // 2 - new sectors2/xxx/zzz/
3228 // If we load from anything but the latest structure, we will
3229 // immediately save to the new one, and remove the old.
3231 std::string sectordir1 = getSectorDir(p2d, 1);
3232 std::string sectordir;
3233 if(fs::PathExists(sectordir1))
3235 sectordir = sectordir1;
3240 sectordir = getSectorDir(p2d, 2);
3244 loadSectorMeta(sectordir, loadlayout != 2);
3246 catch(InvalidFilenameException &e)
3250 catch(FileNotGoodException &e)
3254 catch(std::exception &e)
3263 bool ServerMap::loadSectorFull(v2s16 p2d)
3265 DSTACK(__FUNCTION_NAME);
3267 MapSector *sector = NULL;
3269 // The directory layout we're going to load from.
3270 // 1 - original sectors/xxxxzzzz/
3271 // 2 - new sectors2/xxx/zzz/
3272 // If we load from anything but the latest structure, we will
3273 // immediately save to the new one, and remove the old.
3275 std::string sectordir1 = getSectorDir(p2d, 1);
3276 std::string sectordir;
3277 if(fs::PathExists(sectordir1))
3279 sectordir = sectordir1;
3284 sectordir = getSectorDir(p2d, 2);
3288 sector = loadSectorMeta(sectordir, loadlayout != 2);
3290 catch(InvalidFilenameException &e)
3294 catch(FileNotGoodException &e)
3298 catch(std::exception &e)
3306 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3308 std::vector<fs::DirListNode>::iterator i2;
3309 for(i2=list2.begin(); i2!=list2.end(); i2++)
3315 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3317 catch(InvalidFilenameException &e)
3319 // This catches unknown crap in directory
3325 infostream<<"Sector converted to new layout - deleting "<<
3326 sectordir1<<std::endl;
3327 fs::RecursiveDelete(sectordir1);
3334 void ServerMap::beginSave()
3339 void ServerMap::endSave()
3344 bool ServerMap::saveBlock(MapBlock *block)
3346 return saveBlock(block, dbase);
3349 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3351 v3s16 p3d = block->getPos();
3353 // Dummy blocks are not written
3354 if (block->isDummy()) {
3355 errorstream << "WARNING: saveBlock: Not writing dummy block "
3356 << PP(p3d) << std::endl;
3360 // Format used for writing
3361 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3364 [0] u8 serialization version
3367 std::ostringstream o(std::ios_base::binary);
3368 o.write((char*) &version, 1);
3369 block->serialize(o, version, true);
3371 std::string data = o.str();
3372 bool ret = db->saveBlock(p3d, data);
3374 // We just wrote it to the disk so clear modified flag
3375 block->resetModified();
3380 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3381 MapSector *sector, bool save_after_load)
3383 DSTACK(__FUNCTION_NAME);
3385 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3388 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3389 if(is.good() == false)
3390 throw FileNotGoodException("Cannot open block file");
3392 v3s16 p3d = getBlockPos(sectordir, blockfile);
3393 v2s16 p2d(p3d.X, p3d.Z);
3395 assert(sector->getPos() == p2d);
3397 u8 version = SER_FMT_VER_INVALID;
3398 is.read((char*)&version, 1);
3401 throw SerializationError("ServerMap::loadBlock(): Failed"
3402 " to read MapBlock version");
3404 /*u32 block_size = MapBlock::serializedLength(version);
3405 SharedBuffer<u8> data(block_size);
3406 is.read((char*)*data, block_size);*/
3408 // This will always return a sector because we're the server
3409 //MapSector *sector = emergeSector(p2d);
3411 MapBlock *block = NULL;
3412 bool created_new = false;
3413 block = sector->getBlockNoCreateNoEx(p3d.Y);
3416 block = sector->createBlankBlockNoInsert(p3d.Y);
3421 block->deSerialize(is, version, true);
3423 // If it's a new block, insert it to the map
3425 sector->insertBlock(block);
3428 Save blocks loaded in old format in new format
3431 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3435 // Should be in database now, so delete the old file
3436 fs::RecursiveDelete(fullpath);
3439 // We just loaded it from the disk, so it's up-to-date.
3440 block->resetModified();
3443 catch(SerializationError &e)
3445 infostream<<"WARNING: Invalid block data on disk "
3446 <<"fullpath="<<fullpath
3447 <<" (SerializationError). "
3448 <<"what()="<<e.what()
3450 // Ignoring. A new one will be generated.
3453 // TODO: Backup file; name is in fullpath.
3457 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3459 DSTACK(__FUNCTION_NAME);
3462 std::istringstream is(*blob, std::ios_base::binary);
3464 u8 version = SER_FMT_VER_INVALID;
3465 is.read((char*)&version, 1);
3468 throw SerializationError("ServerMap::loadBlock(): Failed"
3469 " to read MapBlock version");
3471 /*u32 block_size = MapBlock::serializedLength(version);
3472 SharedBuffer<u8> data(block_size);
3473 is.read((char*)*data, block_size);*/
3475 // This will always return a sector because we're the server
3476 //MapSector *sector = emergeSector(p2d);
3478 MapBlock *block = NULL;
3479 bool created_new = false;
3480 block = sector->getBlockNoCreateNoEx(p3d.Y);
3483 block = sector->createBlankBlockNoInsert(p3d.Y);
3488 block->deSerialize(is, version, true);
3490 // If it's a new block, insert it to the map
3492 sector->insertBlock(block);
3495 Save blocks loaded in old format in new format
3498 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3499 // Only save if asked to; no need to update version
3503 // We just loaded it from, so it's up-to-date.
3504 block->resetModified();
3507 catch(SerializationError &e)
3509 errorstream<<"Invalid block data in database"
3510 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3511 <<" (SerializationError): "<<e.what()<<std::endl;
3513 // TODO: Block should be marked as invalid in memory so that it is
3514 // not touched but the game can run
3516 if(g_settings->getBool("ignore_world_load_errors")){
3517 errorstream<<"Ignoring block load error. Duck and cover! "
3518 <<"(ignore_world_load_errors)"<<std::endl;
3520 throw SerializationError("Invalid block data in database");
3526 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3528 DSTACK(__FUNCTION_NAME);
3530 v2s16 p2d(blockpos.X, blockpos.Z);
3534 ret = dbase->loadBlock(blockpos);
3536 loadBlock(&ret, blockpos, createSector(p2d), false);
3537 return getBlockNoCreateNoEx(blockpos);
3539 // Not found in database, try the files
3541 // The directory layout we're going to load from.
3542 // 1 - original sectors/xxxxzzzz/
3543 // 2 - new sectors2/xxx/zzz/
3544 // If we load from anything but the latest structure, we will
3545 // immediately save to the new one, and remove the old.
3547 std::string sectordir1 = getSectorDir(p2d, 1);
3548 std::string sectordir;
3549 if(fs::PathExists(sectordir1))
3551 sectordir = sectordir1;
3556 sectordir = getSectorDir(p2d, 2);
3560 Make sure sector is loaded
3562 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3566 sector = loadSectorMeta(sectordir, loadlayout != 2);
3568 catch(InvalidFilenameException &e)
3572 catch(FileNotGoodException &e)
3576 catch(std::exception &e)
3583 Make sure file exists
3586 std::string blockfilename = getBlockFilename(blockpos);
3587 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3591 Load block and save it to the database
3593 loadBlock(sectordir, blockfilename, sector, true);
3594 return getBlockNoCreateNoEx(blockpos);
3597 bool ServerMap::deleteBlock(v3s16 blockpos)
3599 if (!dbase->deleteBlock(blockpos))
3602 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3604 v2s16 p2d(blockpos.X, blockpos.Z);
3605 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3608 sector->deleteBlock(block);
3614 void ServerMap::PrintInfo(std::ostream &out)
3619 MMVManip::MMVManip(Map *map):
3622 m_create_area(false),
3627 MMVManip::~MMVManip()
3631 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3632 bool load_if_inexistent)
3634 TimeTaker timer1("initialEmerge", &emerge_time);
3636 // Units of these are MapBlocks
3637 v3s16 p_min = blockpos_min;
3638 v3s16 p_max = blockpos_max;
3640 VoxelArea block_area_nodes
3641 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3643 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3646 infostream<<"initialEmerge: area: ";
3647 block_area_nodes.print(infostream);
3648 infostream<<" ("<<size_MB<<"MB)";
3649 infostream<<std::endl;
3652 addArea(block_area_nodes);
3654 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3655 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3656 for(s32 x=p_min.X; x<=p_max.X; x++)
3661 std::map<v3s16, u8>::iterator n;
3662 n = m_loaded_blocks.find(p);
3663 if(n != m_loaded_blocks.end())
3666 bool block_data_inexistent = false;
3669 TimeTaker timer1("emerge load", &emerge_load_time);
3671 block = m_map->getBlockNoCreate(p);
3672 if(block->isDummy())
3673 block_data_inexistent = true;
3675 block->copyTo(*this);
3677 catch(InvalidPositionException &e)
3679 block_data_inexistent = true;
3682 if(block_data_inexistent)
3685 if (load_if_inexistent) {
3686 ServerMap *svrmap = (ServerMap *)m_map;
3687 block = svrmap->emergeBlock(p, false);
3689 block = svrmap->createBlock(p);
3690 block->copyTo(*this);
3692 flags |= VMANIP_BLOCK_DATA_INEXIST;
3695 Mark area inexistent
3697 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3698 // Fill with VOXELFLAG_NO_DATA
3699 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3700 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3702 s32 i = m_area.index(a.MinEdge.X,y,z);
3703 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3707 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3709 // Mark that block was loaded as blank
3710 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3713 m_loaded_blocks[p] = flags;
3719 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3720 bool overwrite_generated)
3722 if(m_area.getExtent() == v3s16(0,0,0))
3726 Copy data of all blocks
3728 for(std::map<v3s16, u8>::iterator
3729 i = m_loaded_blocks.begin();
3730 i != m_loaded_blocks.end(); ++i)
3733 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3734 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3735 if ((existed == false) || (block == NULL) ||
3736 (overwrite_generated == false && block->isGenerated() == true))
3739 block->copyFrom(*this);
3742 (*modified_blocks)[p] = block;