3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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"
29 #include "nodemetadata.h"
36 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
42 If map.sqlite does not exist in the save dir
43 or the block was not found in the database
44 the map will try to load from sectors folder.
45 In either case, map.sqlite will be created
46 and all future saves will save there.
48 Structure of map.sqlite:
59 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 /*m_sector_mutex.Init();
65 assert(m_sector_mutex.IsInitialized());*/
73 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
74 for(; i.atEnd() == false; i++)
76 MapSector *sector = i.getNode()->getValue();
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver, false);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 if(m_event_receivers.find(event_receiver) == NULL)
90 m_event_receivers.remove(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(core::map<MapEventReceiver*, bool>::Iterator
96 i = m_event_receivers.getIterator();
97 i.atEnd()==false; i++)
99 MapEventReceiver* event_receiver = i.getNode()->getKey();
100 event_receiver->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 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
116 MapSector *sector = n->getValue();
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)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 core::map<v3s16, u8> & from_nodes,
237 core::map<v3s16, bool> & light_sources,
238 core::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 core::map<v3s16, u8> unlighted_nodes;
257 core::map<v3s16, u8>::Iterator j;
258 j = from_nodes.getIterator();
261 Initialize block cache
264 MapBlock *block = NULL;
265 // Cache this a bit, too
266 bool block_checked_in_modified = false;
268 for(; j.atEnd() == false; j++)
270 v3s16 pos = j.getNode()->getKey();
271 v3s16 blockpos = getNodeBlockPos(pos);
273 // Only fetch a new block if the block position has changed
275 if(block == NULL || blockpos != blockpos_last){
276 block = getBlockNoCreate(blockpos);
277 blockpos_last = blockpos;
279 block_checked_in_modified = false;
283 catch(InvalidPositionException &e)
291 // Calculate relative position in block
292 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
294 // Get node straight from the block
295 MapNode n = block->getNode(relpos);
297 u8 oldlight = j.getNode()->getValue();
299 // Loop through 6 neighbors
300 for(u16 i=0; i<6; i++)
302 // Get the position of the neighbor node
303 v3s16 n2pos = pos + dirs[i];
305 // Get the block where the node is located
306 v3s16 blockpos = getNodeBlockPos(n2pos);
310 // Only fetch a new block if the block position has changed
312 if(block == NULL || blockpos != blockpos_last){
313 block = getBlockNoCreate(blockpos);
314 blockpos_last = blockpos;
316 block_checked_in_modified = false;
320 catch(InvalidPositionException &e)
325 // Calculate relative position in block
326 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
327 // Get node straight from the block
328 MapNode n2 = block->getNode(relpos);
330 bool changed = false;
332 //TODO: Optimize output by optimizing light_sources?
335 If the neighbor is dimmer than what was specified
336 as oldlight (the light of the previous node)
338 if(n2.getLight(bank, nodemgr) < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates
344 && n2.getLight(bank, nodemgr) != 0)
347 Set light to 0 and add to queue
350 u8 current_light = n2.getLight(bank, nodemgr);
351 n2.setLight(bank, 0, nodemgr);
352 block->setNode(relpos, n2);
354 unlighted_nodes.insert(n2pos, current_light);
358 Remove from light_sources if it is there
359 NOTE: This doesn't happen nearly at all
361 /*if(light_sources.find(n2pos))
363 infostream<<"Removed from light_sources"<<std::endl;
364 light_sources.remove(n2pos);
369 if(light_sources.find(n2pos) != NULL)
370 light_sources.remove(n2pos);*/
373 light_sources.insert(n2pos, true);
376 // Add to modified_blocks
377 if(changed == true && block_checked_in_modified == false)
379 // If the block is not found in modified_blocks, add.
380 if(modified_blocks.find(blockpos) == NULL)
382 modified_blocks.insert(blockpos, block);
384 block_checked_in_modified = true;
387 catch(InvalidPositionException &e)
394 /*infostream<<"unspreadLight(): Changed block "
395 <<blockchangecount<<" times"
396 <<" for "<<from_nodes.size()<<" nodes"
399 if(unlighted_nodes.size() > 0)
400 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
404 A single-node wrapper of the above
406 void Map::unLightNeighbors(enum LightBank bank,
407 v3s16 pos, u8 lightwas,
408 core::map<v3s16, bool> & light_sources,
409 core::map<v3s16, MapBlock*> & modified_blocks)
411 core::map<v3s16, u8> from_nodes;
412 from_nodes.insert(pos, lightwas);
414 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 core::map<v3s16, bool> & from_nodes,
423 core::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.size() == 0)
439 u32 blockchangecount = 0;
441 core::map<v3s16, bool> lighted_nodes;
442 core::map<v3s16, bool>::Iterator j;
443 j = from_nodes.getIterator();
446 Initialize block cache
449 MapBlock *block = NULL;
450 // Cache this a bit, too
451 bool block_checked_in_modified = false;
453 for(; j.atEnd() == false; j++)
454 //for(; j != from_nodes.end(); j++)
456 v3s16 pos = j.getNode()->getKey();
458 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
459 v3s16 blockpos = getNodeBlockPos(pos);
461 // Only fetch a new block if the block position has changed
463 if(block == NULL || blockpos != blockpos_last){
464 block = getBlockNoCreate(blockpos);
465 blockpos_last = blockpos;
467 block_checked_in_modified = false;
471 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
482 // Get node straight from the block
483 MapNode n = block->getNode(relpos);
485 u8 oldlight = n.getLight(bank, nodemgr);
486 u8 newlight = diminish_light(oldlight);
488 // Loop through 6 neighbors
489 for(u16 i=0; i<6; i++){
490 // Get the position of the neighbor node
491 v3s16 n2pos = pos + dirs[i];
493 // Get the block where the node is located
494 v3s16 blockpos = getNodeBlockPos(n2pos);
498 // Only fetch a new block if the block position has changed
500 if(block == NULL || blockpos != blockpos_last){
501 block = getBlockNoCreate(blockpos);
502 blockpos_last = blockpos;
504 block_checked_in_modified = false;
508 catch(InvalidPositionException &e)
513 // Calculate relative position in block
514 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos);
518 bool changed = false;
520 If the neighbor is brighter than the current node,
521 add to list (it will light up this node on its turn)
523 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
525 lighted_nodes.insert(n2pos, true);
526 //lighted_nodes.push_back(n2pos);
530 If the neighbor is dimmer than how much light this node
531 would spread on it, add to list
533 if(n2.getLight(bank, nodemgr) < newlight)
535 if(nodemgr->get(n2).light_propagates)
537 n2.setLight(bank, newlight, nodemgr);
538 block->setNode(relpos, n2);
539 lighted_nodes.insert(n2pos, true);
540 //lighted_nodes.push_back(n2pos);
545 // Add to modified_blocks
546 if(changed == true && block_checked_in_modified == false)
548 // If the block is not found in modified_blocks, add.
549 if(modified_blocks.find(blockpos) == NULL)
551 modified_blocks.insert(blockpos, block);
553 block_checked_in_modified = true;
556 catch(InvalidPositionException &e)
563 /*infostream<<"spreadLight(): Changed block "
564 <<blockchangecount<<" times"
565 <<" for "<<from_nodes.size()<<" nodes"
568 if(lighted_nodes.size() > 0)
569 spreadLight(bank, lighted_nodes, modified_blocks);
573 A single-node source variation of the above.
575 void Map::lightNeighbors(enum LightBank bank,
577 core::map<v3s16, MapBlock*> & modified_blocks)
579 core::map<v3s16, bool> from_nodes;
580 from_nodes.insert(pos, true);
581 spreadLight(bank, from_nodes, modified_blocks);
584 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
586 INodeDefManager *nodemgr = m_gamedef->ndef();
589 v3s16(0,0,1), // back
591 v3s16(1,0,0), // right
592 v3s16(0,0,-1), // front
593 v3s16(0,-1,0), // bottom
594 v3s16(-1,0,0), // left
597 u8 brightest_light = 0;
598 v3s16 brightest_pos(0,0,0);
599 bool found_something = false;
601 // Loop through 6 neighbors
602 for(u16 i=0; i<6; i++){
603 // Get the position of the neighbor node
604 v3s16 n2pos = p + dirs[i];
609 catch(InvalidPositionException &e)
613 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
614 brightest_light = n2.getLight(bank, nodemgr);
615 brightest_pos = n2pos;
616 found_something = true;
620 if(found_something == false)
621 throw InvalidPositionException();
623 return brightest_pos;
627 Propagates sunlight down from a node.
628 Starting point gets sunlight.
630 Returns the lowest y value of where the sunlight went.
632 Mud is turned into grass in where the sunlight stops.
634 s16 Map::propagateSunlight(v3s16 start,
635 core::map<v3s16, MapBlock*> & modified_blocks)
637 INodeDefManager *nodemgr = m_gamedef->ndef();
642 v3s16 pos(start.X, y, start.Z);
644 v3s16 blockpos = getNodeBlockPos(pos);
647 block = getBlockNoCreate(blockpos);
649 catch(InvalidPositionException &e)
654 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
655 MapNode n = block->getNode(relpos);
657 if(nodemgr->get(n).sunlight_propagates)
659 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660 block->setNode(relpos, n);
662 modified_blocks.insert(blockpos, block);
666 // Sunlight goes no further
673 void Map::updateLighting(enum LightBank bank,
674 core::map<v3s16, MapBlock*> & a_blocks,
675 core::map<v3s16, MapBlock*> & modified_blocks)
677 INodeDefManager *nodemgr = m_gamedef->ndef();
679 /*m_dout<<DTIME<<"Map::updateLighting(): "
680 <<a_blocks.size()<<" blocks."<<std::endl;*/
682 //TimeTaker timer("updateLighting");
686 //u32 count_was = modified_blocks.size();
688 core::map<v3s16, MapBlock*> blocks_to_update;
690 core::map<v3s16, bool> light_sources;
692 core::map<v3s16, u8> unlight_from;
695 //TimeTaker t("first stuff");
697 core::map<v3s16, MapBlock*>::Iterator i;
698 i = a_blocks.getIterator();
699 for(; i.atEnd() == false; i++)
701 MapBlock *block = i.getNode()->getValue();
705 // Don't bother with dummy blocks.
709 v3s16 pos = block->getPos();
710 v3s16 posnodes = block->getPosRelative();
711 modified_blocks.insert(pos, block);
713 blocks_to_update.insert(pos, block);
716 Clear all light from block
718 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
719 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
720 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
725 MapNode n = block->getNode(p);
726 u8 oldlight = n.getLight(bank, nodemgr);
727 n.setLight(bank, 0, nodemgr);
728 block->setNode(p, n);
730 // If node sources light, add to list
731 u8 source = nodemgr->get(n).light_source;
733 light_sources[p + posnodes] = true;
735 // Collect borders for unlighting
736 if((x==0 || x == MAP_BLOCKSIZE-1
737 || y==0 || y == MAP_BLOCKSIZE-1
738 || z==0 || z == MAP_BLOCKSIZE-1)
741 v3s16 p_map = p + posnodes;
742 unlight_from.insert(p_map, oldlight);
745 catch(InvalidPositionException &e)
748 This would happen when dealing with a
752 infostream<<"updateLighting(): InvalidPositionException"
757 if(bank == LIGHTBANK_DAY)
759 bool bottom_valid = block->propagateSunlight(light_sources);
761 // If bottom is valid, we're done.
765 else if(bank == LIGHTBANK_NIGHT)
767 // For night lighting, sunlight is not propagated
772 // Invalid lighting bank
776 /*infostream<<"Bottom for sunlight-propagated block ("
777 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
780 // Bottom sunlight is not valid; get the block and loop to it
784 block = getBlockNoCreate(pos);
786 catch(InvalidPositionException &e)
797 Enable this to disable proper lighting for speeding up map
798 generation for testing or whatever
801 //if(g_settings->get(""))
803 core::map<v3s16, MapBlock*>::Iterator i;
804 i = blocks_to_update.getIterator();
805 for(; i.atEnd() == false; i++)
807 MapBlock *block = i.getNode()->getValue();
808 v3s16 p = block->getPos();
809 block->setLightingExpired(false);
817 //TimeTaker timer("unspreadLight");
818 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
823 u32 diff = modified_blocks.size() - count_was;
824 count_was = modified_blocks.size();
825 infostream<<"unspreadLight modified "<<diff<<std::endl;
829 //TimeTaker timer("spreadLight");
830 spreadLight(bank, light_sources, modified_blocks);
835 u32 diff = modified_blocks.size() - count_was;
836 count_was = modified_blocks.size();
837 infostream<<"spreadLight modified "<<diff<<std::endl;
843 //MapVoxelManipulator vmanip(this);
845 // Make a manual voxel manipulator and load all the blocks
846 // that touch the requested blocks
847 ManualMapVoxelManipulator vmanip(this);
850 //TimeTaker timer("initialEmerge");
852 core::map<v3s16, MapBlock*>::Iterator i;
853 i = blocks_to_update.getIterator();
854 for(; i.atEnd() == false; i++)
856 MapBlock *block = i.getNode()->getValue();
857 v3s16 p = block->getPos();
859 // Add all surrounding blocks
860 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
863 Add all surrounding blocks that have up-to-date lighting
864 NOTE: This doesn't quite do the job (not everything
865 appropriate is lighted)
867 /*for(s16 z=-1; z<=1; z++)
868 for(s16 y=-1; y<=1; y++)
869 for(s16 x=-1; x<=1; x++)
871 v3s16 p2 = p + v3s16(x,y,z);
872 MapBlock *block = getBlockNoCreateNoEx(p2);
877 if(block->getLightingExpired())
879 vmanip.initialEmerge(p2, p2);
882 // Lighting of block will be updated completely
883 block->setLightingExpired(false);
888 TimeTaker timer("unSpreadLight");
889 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
892 TimeTaker timer("spreadLight");
893 vmanip.spreadLight(bank, light_sources, nodemgr);
896 TimeTaker timer("blitBack");
897 vmanip.blitBack(modified_blocks);
899 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
904 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
907 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
908 core::map<v3s16, MapBlock*> & modified_blocks)
910 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
911 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
914 Update information about whether day and night light differ
916 for(core::map<v3s16, MapBlock*>::Iterator
917 i = modified_blocks.getIterator();
918 i.atEnd() == false; i++)
920 MapBlock *block = i.getNode()->getValue();
921 block->updateDayNightDiff();
927 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
928 core::map<v3s16, MapBlock*> &modified_blocks)
930 INodeDefManager *nodemgr = m_gamedef->ndef();
933 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
934 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
937 From this node to nodes underneath:
938 If lighting is sunlight (1.0), unlight neighbours and
943 v3s16 toppos = p + v3s16(0,1,0);
944 v3s16 bottompos = p + v3s16(0,-1,0);
946 bool node_under_sunlight = true;
947 core::map<v3s16, bool> light_sources;
950 If there is a node at top and it doesn't have sunlight,
951 there has not been any sunlight going down.
953 Otherwise there probably is.
956 MapNode topnode = getNode(toppos);
958 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
959 node_under_sunlight = false;
961 catch(InvalidPositionException &e)
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 = getNode(p).getLight(bank, nodemgr);
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.insert(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, nodemgr);
999 If node lets sunlight through and is under sunlight, it has
1002 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1004 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1008 Set the node on the map
1017 std::string metadata_name = nodemgr->get(n).metadata_name;
1018 if(metadata_name != ""){
1019 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1021 errorstream<<"Failed to create node metadata \""
1022 <<metadata_name<<"\""<<std::endl;
1024 setNodeMetadata(p, meta);
1029 If node is under sunlight and doesn't let sunlight through,
1030 take all sunlighted nodes under it and clear light from them
1031 and from where the light has been spread.
1032 TODO: This could be optimized by mass-unlighting instead
1035 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1039 //m_dout<<DTIME<<"y="<<y<<std::endl;
1040 v3s16 n2pos(p.X, y, p.Z);
1044 n2 = getNode(n2pos);
1046 catch(InvalidPositionException &e)
1051 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1053 unLightNeighbors(LIGHTBANK_DAY,
1054 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1055 light_sources, modified_blocks);
1056 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1064 for(s32 i=0; i<2; i++)
1066 enum LightBank bank = banks[i];
1069 Spread light from all nodes that might be capable of doing so
1071 spreadLight(bank, light_sources, modified_blocks);
1075 Update information about whether day and night light differ
1077 for(core::map<v3s16, MapBlock*>::Iterator
1078 i = modified_blocks.getIterator();
1079 i.atEnd() == false; i++)
1081 MapBlock *block = i.getNode()->getValue();
1082 block->updateDayNightDiff();
1086 Add neighboring liquid nodes and the node itself if it is
1087 liquid (=water node was added) to transform queue.
1090 v3s16(0,0,0), // self
1091 v3s16(0,0,1), // back
1092 v3s16(0,1,0), // top
1093 v3s16(1,0,0), // right
1094 v3s16(0,0,-1), // front
1095 v3s16(0,-1,0), // bottom
1096 v3s16(-1,0,0), // left
1098 for(u16 i=0; i<7; i++)
1103 v3s16 p2 = p + dirs[i];
1105 MapNode n2 = getNode(p2);
1106 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1108 m_transforming_liquid.push_back(p2);
1111 }catch(InvalidPositionException &e)
1119 void Map::removeNodeAndUpdate(v3s16 p,
1120 core::map<v3s16, MapBlock*> &modified_blocks)
1122 INodeDefManager *nodemgr = m_gamedef->ndef();
1124 /*PrintInfo(m_dout);
1125 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1126 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1128 bool node_under_sunlight = true;
1130 v3s16 toppos = p + v3s16(0,1,0);
1132 // Node will be replaced with this
1133 content_t replace_material = CONTENT_AIR;
1136 If there is a node at top and it doesn't have sunlight,
1137 there will be no sunlight going down.
1140 MapNode topnode = getNode(toppos);
1142 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1143 node_under_sunlight = false;
1145 catch(InvalidPositionException &e)
1149 core::map<v3s16, bool> light_sources;
1151 enum LightBank banks[] =
1156 for(s32 i=0; i<2; i++)
1158 enum LightBank bank = banks[i];
1161 Unlight neighbors (in case the node is a light source)
1163 unLightNeighbors(bank, p,
1164 getNode(p).getLight(bank, nodemgr),
1165 light_sources, modified_blocks);
1169 Remove node metadata
1172 removeNodeMetadata(p);
1176 This also clears the lighting.
1180 n.setContent(replace_material);
1183 for(s32 i=0; i<2; i++)
1185 enum LightBank bank = banks[i];
1188 Recalculate lighting
1190 spreadLight(bank, light_sources, modified_blocks);
1193 // Add the block of the removed node to modified_blocks
1194 v3s16 blockpos = getNodeBlockPos(p);
1195 MapBlock * block = getBlockNoCreate(blockpos);
1196 assert(block != NULL);
1197 modified_blocks.insert(blockpos, block);
1200 If the removed node was under sunlight, propagate the
1201 sunlight down from it and then light all neighbors
1202 of the propagated blocks.
1204 if(node_under_sunlight)
1206 s16 ybottom = propagateSunlight(p, modified_blocks);
1207 /*m_dout<<DTIME<<"Node was under sunlight. "
1208 "Propagating sunlight";
1209 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1211 for(; y >= ybottom; y--)
1213 v3s16 p2(p.X, y, p.Z);
1214 /*m_dout<<DTIME<<"lighting neighbors of node ("
1215 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1217 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1222 // Set the lighting of this node to 0
1223 // TODO: Is this needed? Lighting is cleared up there already.
1225 MapNode n = getNode(p);
1226 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1229 catch(InvalidPositionException &e)
1235 for(s32 i=0; i<2; i++)
1237 enum LightBank bank = banks[i];
1239 // Get the brightest neighbour node and propagate light from it
1240 v3s16 n2p = getBrightestNeighbour(bank, p);
1242 MapNode n2 = getNode(n2p);
1243 lightNeighbors(bank, n2p, modified_blocks);
1245 catch(InvalidPositionException &e)
1251 Update information about whether day and night light differ
1253 for(core::map<v3s16, MapBlock*>::Iterator
1254 i = modified_blocks.getIterator();
1255 i.atEnd() == false; i++)
1257 MapBlock *block = i.getNode()->getValue();
1258 block->updateDayNightDiff();
1262 Add neighboring liquid nodes and this node to transform queue.
1263 (it's vital for the node itself to get updated last.)
1266 v3s16(0,0,1), // back
1267 v3s16(0,1,0), // top
1268 v3s16(1,0,0), // right
1269 v3s16(0,0,-1), // front
1270 v3s16(0,-1,0), // bottom
1271 v3s16(-1,0,0), // left
1272 v3s16(0,0,0), // self
1274 for(u16 i=0; i<7; i++)
1279 v3s16 p2 = p + dirs[i];
1281 MapNode n2 = getNode(p2);
1282 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1284 m_transforming_liquid.push_back(p2);
1287 }catch(InvalidPositionException &e)
1293 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1296 event.type = MEET_ADDNODE;
1300 bool succeeded = true;
1302 core::map<v3s16, MapBlock*> modified_blocks;
1303 addNodeAndUpdate(p, n, modified_blocks);
1305 // Copy modified_blocks to event
1306 for(core::map<v3s16, MapBlock*>::Iterator
1307 i = modified_blocks.getIterator();
1308 i.atEnd()==false; i++)
1310 event.modified_blocks.insert(i.getNode()->getKey(), false);
1313 catch(InvalidPositionException &e){
1317 dispatchEvent(&event);
1322 bool Map::removeNodeWithEvent(v3s16 p)
1325 event.type = MEET_REMOVENODE;
1328 bool succeeded = true;
1330 core::map<v3s16, MapBlock*> modified_blocks;
1331 removeNodeAndUpdate(p, modified_blocks);
1333 // Copy modified_blocks to event
1334 for(core::map<v3s16, MapBlock*>::Iterator
1335 i = modified_blocks.getIterator();
1336 i.atEnd()==false; i++)
1338 event.modified_blocks.insert(i.getNode()->getKey(), false);
1341 catch(InvalidPositionException &e){
1345 dispatchEvent(&event);
1350 bool Map::dayNightDiffed(v3s16 blockpos)
1353 v3s16 p = blockpos + v3s16(0,0,0);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1361 v3s16 p = blockpos + v3s16(-1,0,0);
1362 MapBlock *b = getBlockNoCreate(p);
1363 if(b->dayNightDiffed())
1366 catch(InvalidPositionException &e){}
1368 v3s16 p = blockpos + v3s16(0,-1,0);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->dayNightDiffed())
1373 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(0,0,-1);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1383 v3s16 p = blockpos + v3s16(1,0,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->dayNightDiffed())
1388 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(0,1,0);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->dayNightDiffed())
1395 catch(InvalidPositionException &e){}
1397 v3s16 p = blockpos + v3s16(0,0,1);
1398 MapBlock *b = getBlockNoCreate(p);
1399 if(b->dayNightDiffed())
1402 catch(InvalidPositionException &e){}
1408 Updates usage timers
1410 void Map::timerUpdate(float dtime, float unload_timeout,
1411 core::list<v3s16> *unloaded_blocks)
1413 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1415 // Profile modified reasons
1416 Profiler modprofiler;
1418 core::list<v2s16> sector_deletion_queue;
1419 u32 deleted_blocks_count = 0;
1420 u32 saved_blocks_count = 0;
1421 u32 block_count_all = 0;
1423 core::map<v2s16, MapSector*>::Iterator si;
1426 si = m_sectors.getIterator();
1427 for(; si.atEnd() == false; si++)
1429 MapSector *sector = si.getNode()->getValue();
1431 bool all_blocks_deleted = true;
1433 core::list<MapBlock*> blocks;
1434 sector->getBlocks(blocks);
1436 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1437 i != blocks.end(); i++)
1439 MapBlock *block = (*i);
1441 block->incrementUsageTimer(dtime);
1443 if(block->getUsageTimer() > unload_timeout)
1445 v3s16 p = block->getPos();
1448 if(block->getModified() != MOD_STATE_CLEAN
1449 && save_before_unloading)
1451 modprofiler.add(block->getModifiedReason(), 1);
1453 saved_blocks_count++;
1456 // Delete from memory
1457 sector->deleteBlock(block);
1460 unloaded_blocks->push_back(p);
1462 deleted_blocks_count++;
1466 all_blocks_deleted = false;
1471 if(all_blocks_deleted)
1473 sector_deletion_queue.push_back(si.getNode()->getKey());
1478 // Finally delete the empty sectors
1479 deleteSectors(sector_deletion_queue);
1481 if(deleted_blocks_count != 0)
1483 PrintInfo(infostream); // ServerMap/ClientMap:
1484 infostream<<"Unloaded "<<deleted_blocks_count
1485 <<" blocks from memory";
1486 if(save_before_unloading)
1487 infostream<<", of which "<<saved_blocks_count<<" were written";
1488 infostream<<", "<<block_count_all<<" blocks in memory";
1489 infostream<<"."<<std::endl;
1490 if(saved_blocks_count != 0){
1491 PrintInfo(infostream); // ServerMap/ClientMap:
1492 infostream<<"Blocks modified by: "<<std::endl;
1493 modprofiler.print(infostream);
1498 void Map::deleteSectors(core::list<v2s16> &list)
1500 core::list<v2s16>::Iterator j;
1501 for(j=list.begin(); j!=list.end(); j++)
1503 MapSector *sector = m_sectors[*j];
1504 // If sector is in sector cache, remove it from there
1505 if(m_sector_cache == sector)
1506 m_sector_cache = NULL;
1507 // Remove from map and delete
1508 m_sectors.remove(*j);
1514 void Map::unloadUnusedData(float timeout,
1515 core::list<v3s16> *deleted_blocks)
1517 core::list<v2s16> sector_deletion_queue;
1518 u32 deleted_blocks_count = 0;
1519 u32 saved_blocks_count = 0;
1521 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1522 for(; si.atEnd() == false; si++)
1524 MapSector *sector = si.getNode()->getValue();
1526 bool all_blocks_deleted = true;
1528 core::list<MapBlock*> blocks;
1529 sector->getBlocks(blocks);
1530 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1531 i != blocks.end(); i++)
1533 MapBlock *block = (*i);
1535 if(block->getUsageTimer() > timeout)
1538 if(block->getModified() != MOD_STATE_CLEAN)
1541 saved_blocks_count++;
1543 // Delete from memory
1544 sector->deleteBlock(block);
1545 deleted_blocks_count++;
1549 all_blocks_deleted = false;
1553 if(all_blocks_deleted)
1555 sector_deletion_queue.push_back(si.getNode()->getKey());
1559 deleteSectors(sector_deletion_queue);
1561 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1562 <<", of which "<<saved_blocks_count<<" were wr."
1565 //return sector_deletion_queue.getSize();
1566 //return deleted_blocks_count;
1570 void Map::PrintInfo(std::ostream &out)
1575 #define WATER_DROP_BOOST 4
1579 NEIGHBOR_SAME_LEVEL,
1582 struct NodeNeighbor {
1588 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1590 INodeDefManager *nodemgr = m_gamedef->ndef();
1592 DSTACK(__FUNCTION_NAME);
1593 //TimeTaker timer("transformLiquids()");
1596 u32 initial_size = m_transforming_liquid.size();
1598 /*if(initial_size != 0)
1599 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1601 // list of nodes that due to viscosity have not reached their max level height
1602 UniqueQueue<v3s16> must_reflow;
1604 // List of MapBlocks that will require a lighting update (due to lava)
1605 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1607 while(m_transforming_liquid.size() != 0)
1609 // This should be done here so that it is done when continue is used
1610 if(loopcount >= initial_size * 3)
1615 Get a queued transforming liquid node
1617 v3s16 p0 = m_transforming_liquid.pop_front();
1619 MapNode n0 = getNodeNoEx(p0);
1622 Collect information about current node
1624 s8 liquid_level = -1;
1625 u8 liquid_kind = CONTENT_IGNORE;
1626 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1627 switch (liquid_type) {
1629 liquid_level = LIQUID_LEVEL_SOURCE;
1630 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1632 case LIQUID_FLOWING:
1633 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1634 liquid_kind = n0.getContent();
1637 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1638 // continue with the next node.
1639 if (n0.getContent() != CONTENT_AIR)
1641 liquid_kind = CONTENT_AIR;
1646 Collect information about the environment
1648 const v3s16 *dirs = g_6dirs;
1649 NodeNeighbor sources[6]; // surrounding sources
1650 int num_sources = 0;
1651 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1653 NodeNeighbor airs[6]; // surrounding air
1655 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1656 int num_neutrals = 0;
1657 bool flowing_down = false;
1658 for (u16 i = 0; i < 6; i++) {
1659 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1662 nt = NEIGHBOR_UPPER;
1665 nt = NEIGHBOR_LOWER;
1668 v3s16 npos = p0 + dirs[i];
1669 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1670 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1672 if (nb.n.getContent() == CONTENT_AIR) {
1673 airs[num_airs++] = nb;
1674 // if the current node is a water source the neighbor
1675 // should be enqueded for transformation regardless of whether the
1676 // current node changes or not.
1677 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1678 m_transforming_liquid.push_back(npos);
1679 // if the current node happens to be a flowing node, it will start to flow down here.
1680 if (nb.t == NEIGHBOR_LOWER) {
1681 flowing_down = true;
1684 neutrals[num_neutrals++] = nb;
1688 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1689 if (liquid_kind == CONTENT_AIR)
1690 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1691 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1692 neutrals[num_neutrals++] = nb;
1694 // Do not count bottom source, it will screw things up
1696 sources[num_sources++] = nb;
1699 case LIQUID_FLOWING:
1700 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1701 if (liquid_kind == CONTENT_AIR)
1702 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1703 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1704 neutrals[num_neutrals++] = nb;
1706 flows[num_flows++] = nb;
1707 if (nb.t == NEIGHBOR_LOWER)
1708 flowing_down = true;
1715 decide on the type (and possibly level) of the current node
1717 content_t new_node_content;
1718 s8 new_node_level = -1;
1719 s8 max_node_level = -1;
1720 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1721 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1722 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1723 // it's perfectly safe to use liquid_kind here to determine the new node content.
1724 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1725 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1726 // liquid_kind is set properly, see above
1727 new_node_content = liquid_kind;
1728 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1730 // no surrounding sources, so get the maximum level that can flow into this node
1731 for (u16 i = 0; i < num_flows; i++) {
1732 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1733 switch (flows[i].t) {
1734 case NEIGHBOR_UPPER:
1735 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1736 max_node_level = LIQUID_LEVEL_MAX;
1737 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1738 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1739 } else if (nb_liquid_level > max_node_level)
1740 max_node_level = nb_liquid_level;
1742 case NEIGHBOR_LOWER:
1744 case NEIGHBOR_SAME_LEVEL:
1745 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1746 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1747 max_node_level = nb_liquid_level - 1;
1753 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1754 if (viscosity > 1 && max_node_level != liquid_level) {
1755 // amount to gain, limited by viscosity
1756 // must be at least 1 in absolute value
1757 s8 level_inc = max_node_level - liquid_level;
1758 if (level_inc < -viscosity || level_inc > viscosity)
1759 new_node_level = liquid_level + level_inc/viscosity;
1760 else if (level_inc < 0)
1761 new_node_level = liquid_level - 1;
1762 else if (level_inc > 0)
1763 new_node_level = liquid_level + 1;
1764 if (new_node_level != max_node_level)
1765 must_reflow.push_back(p0);
1767 new_node_level = max_node_level;
1769 if (new_node_level >= 0)
1770 new_node_content = liquid_kind;
1772 new_node_content = CONTENT_AIR;
1777 check if anything has changed. if not, just continue with the next node.
1779 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1780 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1781 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1787 update the current node
1789 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1790 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1791 // set level to last 3 bits, flowing down bit to 4th bit
1792 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1794 // set the liquid level and flow bit to 0
1795 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1797 n0.setContent(new_node_content);
1799 v3s16 blockpos = getNodeBlockPos(p0);
1800 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1802 modified_blocks.insert(blockpos, block);
1803 // If node emits light, MapBlock requires lighting update
1804 if(nodemgr->get(n0).light_source != 0)
1805 lighting_modified_blocks[block->getPos()] = block;
1809 enqueue neighbors for update if neccessary
1811 switch (nodemgr->get(n0.getContent()).liquid_type) {
1813 case LIQUID_FLOWING:
1814 // make sure source flows into all neighboring nodes
1815 for (u16 i = 0; i < num_flows; i++)
1816 if (flows[i].t != NEIGHBOR_UPPER)
1817 m_transforming_liquid.push_back(flows[i].p);
1818 for (u16 i = 0; i < num_airs; i++)
1819 if (airs[i].t != NEIGHBOR_UPPER)
1820 m_transforming_liquid.push_back(airs[i].p);
1823 // this flow has turned to air; neighboring flows might need to do the same
1824 for (u16 i = 0; i < num_flows; i++)
1825 m_transforming_liquid.push_back(flows[i].p);
1829 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1830 while (must_reflow.size() > 0)
1831 m_transforming_liquid.push_back(must_reflow.pop_front());
1832 updateLighting(lighting_modified_blocks, modified_blocks);
1835 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1837 v3s16 blockpos = getNodeBlockPos(p);
1838 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1839 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1841 infostream<<"Map::getNodeMetadata(): Need to emerge "
1842 <<PP(blockpos)<<std::endl;
1843 block = emergeBlock(blockpos, false);
1847 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1851 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1855 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1857 v3s16 blockpos = getNodeBlockPos(p);
1858 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1859 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1861 infostream<<"Map::setNodeMetadata(): Need to emerge "
1862 <<PP(blockpos)<<std::endl;
1863 block = emergeBlock(blockpos, false);
1867 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1871 block->m_node_metadata->set(p_rel, meta);
1874 void Map::removeNodeMetadata(v3s16 p)
1876 v3s16 blockpos = getNodeBlockPos(p);
1877 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1878 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1881 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1885 block->m_node_metadata->remove(p_rel);
1888 void Map::nodeMetadataStep(float dtime,
1889 core::map<v3s16, MapBlock*> &changed_blocks)
1893 Currently there is no way to ensure that all the necessary
1894 blocks are loaded when this is run. (They might get unloaded)
1895 NOTE: ^- Actually, that might not be so. In a quick test it
1896 reloaded a block with a furnace when I walked back to it from
1899 core::map<v2s16, MapSector*>::Iterator si;
1900 si = m_sectors.getIterator();
1901 for(; si.atEnd() == false; si++)
1903 MapSector *sector = si.getNode()->getValue();
1904 core::list< MapBlock * > sectorblocks;
1905 sector->getBlocks(sectorblocks);
1906 core::list< MapBlock * >::Iterator i;
1907 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1909 MapBlock *block = *i;
1910 bool changed = block->m_node_metadata->step(dtime);
1912 changed_blocks[block->getPos()] = block;
1921 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1922 Map(dout_server, gamedef),
1924 m_map_metadata_changed(true),
1926 m_database_read(NULL),
1927 m_database_write(NULL)
1929 verbosestream<<__FUNCTION_NAME<<std::endl;
1931 //m_chunksize = 8; // Takes a few seconds
1933 if (g_settings->get("fixed_map_seed").empty())
1935 m_seed = (((u64)(myrand()%0xffff)<<0)
1936 + ((u64)(myrand()%0xffff)<<16)
1937 + ((u64)(myrand()%0xffff)<<32)
1938 + ((u64)(myrand()%0xffff)<<48));
1942 m_seed = g_settings->getU64("fixed_map_seed");
1946 Experimental and debug stuff
1953 Try to load map; if not found, create a new one.
1956 m_savedir = savedir;
1957 m_map_saving_enabled = false;
1961 // If directory exists, check contents and load if possible
1962 if(fs::PathExists(m_savedir))
1964 // If directory is empty, it is safe to save into it.
1965 if(fs::GetDirListing(m_savedir).size() == 0)
1967 infostream<<"ServerMap: Empty save directory is valid."
1969 m_map_saving_enabled = true;
1974 // Load map metadata (seed, chunksize)
1977 catch(FileNotGoodException &e){
1978 infostream<<"WARNING: Could not load map metadata"
1979 //<<" Disabling chunk-based generator."
1984 infostream<<"ServerMap: Successfully loaded map "
1985 <<"metadata from "<<savedir
1986 <<", assuming valid save directory."
1987 <<" seed="<<m_seed<<"."
1990 m_map_saving_enabled = true;
1991 // Map loaded, not creating new one
1995 // If directory doesn't exist, it is safe to save to it
1997 m_map_saving_enabled = true;
2000 catch(std::exception &e)
2002 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2003 <<", exception: "<<e.what()<<std::endl;
2004 infostream<<"Please remove the map or fix it."<<std::endl;
2005 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2008 infostream<<"Initializing new map."<<std::endl;
2010 // Create zero sector
2011 emergeSector(v2s16(0,0));
2013 // Initially write whole map
2014 save(MOD_STATE_CLEAN);
2017 ServerMap::~ServerMap()
2019 verbosestream<<__FUNCTION_NAME<<std::endl;
2023 if(m_map_saving_enabled)
2025 // Save only changed parts
2026 save(MOD_STATE_WRITE_AT_UNLOAD);
2027 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2031 infostream<<"ServerMap: Map not saved"<<std::endl;
2034 catch(std::exception &e)
2036 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2037 <<", exception: "<<e.what()<<std::endl;
2041 Close database if it was opened
2044 sqlite3_finalize(m_database_read);
2045 if(m_database_write)
2046 sqlite3_finalize(m_database_write);
2048 sqlite3_close(m_database);
2054 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2055 for(; i.atEnd() == false; i++)
2057 MapChunk *chunk = i.getNode()->getValue();
2063 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2065 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2066 if(enable_mapgen_debug_info)
2067 infostream<<"initBlockMake(): "
2068 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2069 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2073 v3s16 blockpos_div = getContainerPos(blockpos, chunksize);
2074 v3s16 blockpos_min = blockpos_div * chunksize;
2075 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2077 // Do nothing if not inside limits (+-1 because of neighbors)
2078 if(blockpos_over_limit(blockpos_min - v3s16(1,1,1)) ||
2079 blockpos_over_limit(blockpos_max + v3s16(1,1,1)))
2085 data->no_op = false;
2086 data->seed = m_seed;
2087 data->blockpos_min = blockpos_min;
2088 data->blockpos_max = blockpos_max;
2089 data->blockpos_requested = blockpos;
2090 data->nodedef = m_gamedef->ndef();
2093 Create the whole area of this and the neighboring blocks
2096 //TimeTaker timer("initBlockMake() create area");
2098 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2099 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2101 v2s16 sectorpos(x, z);
2102 // Sector metadata is loaded from disk if not already loaded.
2103 ServerMapSector *sector = createSector(sectorpos);
2106 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2109 //MapBlock *block = createBlock(p);
2110 // 1) get from memory, 2) load from disk
2111 MapBlock *block = emergeBlock(p, false);
2112 // 3) create a blank one
2115 block = createBlock(p);
2118 Block gets sunlight if this is true.
2120 Refer to the map generator heuristics.
2122 bool ug = mapgen::block_is_underground(data->seed, p);
2123 block->setIsUnderground(ug);
2126 // Lighting will not be valid after make_chunk is called
2127 block->setLightingExpired(true);
2128 // Lighting will be calculated
2129 //block->setLightingExpired(false);
2135 Now we have a big empty area.
2137 Make a ManualMapVoxelManipulator that contains this and the
2141 // The area that contains this block and it's neighbors
2142 v3s16 bigarea_blocks_min = blockpos_min - v3s16(1,1,1);
2143 v3s16 bigarea_blocks_max = blockpos_max + v3s16(1,1,1);
2145 data->vmanip = new ManualMapVoxelManipulator(this);
2146 //data->vmanip->setMap(this);
2150 //TimeTaker timer("initBlockMake() initialEmerge");
2151 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2154 // Data is ready now.
2157 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2158 core::map<v3s16, MapBlock*> &changed_blocks)
2160 v3s16 blockpos_min = data->blockpos_min;
2161 v3s16 blockpos_max = data->blockpos_max;
2162 v3s16 blockpos_requested = data->blockpos_requested;
2163 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2164 <<blockpos_requested.Y<<","
2165 <<blockpos_requested.Z<<")"<<std::endl;*/
2169 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2173 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2175 /*infostream<<"Resulting vmanip:"<<std::endl;
2176 data->vmanip.print(infostream);*/
2178 // Make sure affected blocks are loaded
2179 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2180 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2181 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2184 // Load from disk if not already in memory
2185 emergeBlock(p, false);
2189 Blit generated stuff to map
2190 NOTE: blitBackAll adds nearly everything to changed_blocks
2194 //TimeTaker timer("finishBlockMake() blitBackAll");
2195 data->vmanip->blitBackAll(&changed_blocks);
2198 if(enable_mapgen_debug_info)
2199 infostream<<"finishBlockMake: changed_blocks.size()="
2200 <<changed_blocks.size()<<std::endl;
2203 Copy transforming liquid information
2205 while(data->transforming_liquid.size() > 0)
2207 v3s16 p = data->transforming_liquid.pop_front();
2208 m_transforming_liquid.push_back(p);
2212 Do stuff in central blocks
2219 TimeTaker t("finishBlockMake lighting update");
2221 core::map<v3s16, MapBlock*> lighting_update_blocks;
2224 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2225 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2226 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2229 MapBlock *block = getBlockNoCreateNoEx(p);
2231 lighting_update_blocks.insert(block->getPos(), block);
2234 updateLighting(lighting_update_blocks, changed_blocks);
2237 Set lighting to non-expired state in all of them.
2238 This is cheating, but it is not fast enough if all of them
2239 would actually be updated.
2241 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2242 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2243 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2246 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2249 if(enable_mapgen_debug_info == false)
2250 t.stop(true); // Hide output
2254 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2255 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2256 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2259 MapBlock *block = getBlockNoCreateNoEx(p);
2263 Add random objects to block
2265 mapgen::add_random_objects(block);
2269 Go through changed blocks
2271 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2272 i.atEnd() == false; i++)
2274 MapBlock *block = i.getNode()->getValue();
2277 Update day/night difference cache of the MapBlocks
2279 block->updateDayNightDiff();
2281 Set block as modified
2283 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2284 "finishBlockMake updateDayNightDiff");
2288 Set central blocks as generated
2290 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2291 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2292 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2295 MapBlock *block = getBlockNoCreateNoEx(p);
2297 block->setGenerated(true);
2301 Save changed parts of map
2302 NOTE: Will be saved later.
2304 //save(MOD_STATE_WRITE_AT_UNLOAD);
2306 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2307 <<","<<blockpos_requested.Y<<","
2308 <<blockpos_requested.Z<<")"<<std::endl;*/
2310 if(enable_mapgen_debug_info)
2313 Analyze resulting blocks
2315 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2316 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2317 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2318 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2319 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2320 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2322 v3s16 p = v3s16(x,y,z);
2323 MapBlock *block = getBlockNoCreateNoEx(p);
2325 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2326 infostream<<"Generated "<<spos<<": "
2327 <<analyze_block(block)<<std::endl;
2332 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2338 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2340 DSTACKF("%s: p2d=(%d,%d)",
2345 Check if it exists already in memory
2347 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2352 Try to load it from disk (with blocks)
2354 //if(loadSectorFull(p2d) == true)
2357 Try to load metadata from disk
2360 if(loadSectorMeta(p2d) == true)
2362 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2365 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2366 throw InvalidPositionException("");
2372 Do not create over-limit
2374 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2375 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2376 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2377 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2378 throw InvalidPositionException("createSector(): pos. over limit");
2381 Generate blank sector
2384 sector = new ServerMapSector(this, p2d, m_gamedef);
2386 // Sector position on map in nodes
2387 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2392 m_sectors.insert(p2d, sector);
2398 This is a quick-hand function for calling makeBlock().
2400 MapBlock * ServerMap::generateBlock(
2402 core::map<v3s16, MapBlock*> &modified_blocks
2405 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2407 /*infostream<<"generateBlock(): "
2408 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2411 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2413 TimeTaker timer("generateBlock");
2415 //MapBlock *block = original_dummy;
2417 v2s16 p2d(p.X, p.Z);
2418 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2421 Do not generate over-limit
2423 if(blockpos_over_limit(p))
2425 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2426 throw InvalidPositionException("generateBlock(): pos. over limit");
2430 Create block make data
2432 mapgen::BlockMakeData data;
2433 initBlockMake(&data, p);
2439 TimeTaker t("mapgen::make_block()");
2440 mapgen::make_block(&data);
2442 if(enable_mapgen_debug_info == false)
2443 t.stop(true); // Hide output
2447 Blit data back on map, update lighting, add mobs and whatever this does
2449 finishBlockMake(&data, modified_blocks);
2454 MapBlock *block = getBlockNoCreateNoEx(p);
2462 bool erroneus_content = false;
2463 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2464 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2465 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2468 MapNode n = block->getNode(p);
2469 if(n.getContent() == CONTENT_IGNORE)
2471 infostream<<"CONTENT_IGNORE at "
2472 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2474 erroneus_content = true;
2478 if(erroneus_content)
2487 Generate a completely empty block
2491 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2492 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2494 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2497 n.setContent(CONTENT_AIR);
2498 block->setNode(v3s16(x0,y0,z0), n);
2504 if(enable_mapgen_debug_info == false)
2505 timer.stop(true); // Hide output
2510 MapBlock * ServerMap::createBlock(v3s16 p)
2512 DSTACKF("%s: p=(%d,%d,%d)",
2513 __FUNCTION_NAME, p.X, p.Y, p.Z);
2516 Do not create over-limit
2518 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2519 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2520 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2521 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2522 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2523 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2524 throw InvalidPositionException("createBlock(): pos. over limit");
2526 v2s16 p2d(p.X, p.Z);
2529 This will create or load a sector if not found in memory.
2530 If block exists on disk, it will be loaded.
2532 NOTE: On old save formats, this will be slow, as it generates
2533 lighting on blocks for them.
2535 ServerMapSector *sector;
2537 sector = (ServerMapSector*)createSector(p2d);
2538 assert(sector->getId() == MAPSECTOR_SERVER);
2540 catch(InvalidPositionException &e)
2542 infostream<<"createBlock: createSector() failed"<<std::endl;
2546 NOTE: This should not be done, or at least the exception
2547 should not be passed on as std::exception, because it
2548 won't be catched at all.
2550 /*catch(std::exception &e)
2552 infostream<<"createBlock: createSector() failed: "
2553 <<e.what()<<std::endl;
2558 Try to get a block from the sector
2561 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2564 if(block->isDummy())
2569 block = sector->createBlankBlock(block_y);
2573 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2575 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2577 p.X, p.Y, p.Z, allow_generate);
2580 MapBlock *block = getBlockNoCreateNoEx(p);
2581 if(block && block->isDummy() == false)
2586 MapBlock *block = loadBlock(p);
2593 core::map<v3s16, MapBlock*> modified_blocks;
2594 MapBlock *block = generateBlock(p, modified_blocks);
2598 event.type = MEET_OTHER;
2601 // Copy modified_blocks to event
2602 for(core::map<v3s16, MapBlock*>::Iterator
2603 i = modified_blocks.getIterator();
2604 i.atEnd()==false; i++)
2606 event.modified_blocks.insert(i.getNode()->getKey(), false);
2610 dispatchEvent(&event);
2619 s16 ServerMap::findGroundLevel(v2s16 p2d)
2623 Uh, just do something random...
2625 // Find existing map from top to down
2628 v3s16 p(p2d.X, max, p2d.Y);
2629 for(; p.Y>min; p.Y--)
2631 MapNode n = getNodeNoEx(p);
2632 if(n.getContent() != CONTENT_IGNORE)
2637 // If this node is not air, go to plan b
2638 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2640 // Search existing walkable and return it
2641 for(; p.Y>min; p.Y--)
2643 MapNode n = getNodeNoEx(p);
2644 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2653 Determine from map generator noise functions
2656 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2659 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2660 //return (s16)level;
2663 void ServerMap::createDatabase() {
2666 e = sqlite3_exec(m_database,
2667 "CREATE TABLE IF NOT EXISTS `blocks` ("
2668 "`pos` INT NOT NULL PRIMARY KEY,"
2671 , NULL, NULL, NULL);
2672 if(e == SQLITE_ABORT)
2673 throw FileNotGoodException("Could not create database structure");
2675 infostream<<"ServerMap: Database structure was created";
2678 void ServerMap::verifyDatabase() {
2683 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2684 bool needs_create = false;
2688 Open the database connection
2691 createDirs(m_savedir);
2693 if(!fs::PathExists(dbp))
2694 needs_create = true;
2696 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2697 if(d != SQLITE_OK) {
2698 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2699 throw FileNotGoodException("Cannot open database file");
2705 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2706 if(d != SQLITE_OK) {
2707 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2708 throw FileNotGoodException("Cannot prepare read statement");
2711 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2712 if(d != SQLITE_OK) {
2713 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2714 throw FileNotGoodException("Cannot prepare write statement");
2717 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2718 if(d != SQLITE_OK) {
2719 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2720 throw FileNotGoodException("Cannot prepare read statement");
2723 infostream<<"ServerMap: Database opened"<<std::endl;
2727 bool ServerMap::loadFromFolders() {
2728 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2733 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2734 return (sqlite3_int64)pos.Z*16777216 +
2735 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2738 void ServerMap::createDirs(std::string path)
2740 if(fs::CreateAllDirs(path) == false)
2742 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2743 <<"\""<<path<<"\""<<std::endl;
2744 throw BaseException("ServerMap failed to create directory");
2748 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2754 snprintf(cc, 9, "%.4x%.4x",
2755 (unsigned int)pos.X&0xffff,
2756 (unsigned int)pos.Y&0xffff);
2758 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2760 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2761 (unsigned int)pos.X&0xfff,
2762 (unsigned int)pos.Y&0xfff);
2764 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2770 v2s16 ServerMap::getSectorPos(std::string dirname)
2774 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2775 assert(spos != std::string::npos);
2776 if(dirname.size() - spos == 8)
2779 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2781 else if(dirname.size() - spos == 3)
2784 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2785 // Sign-extend the 12 bit values up to 16 bits...
2786 if(x&0x800) x|=0xF000;
2787 if(y&0x800) y|=0xF000;
2794 v2s16 pos((s16)x, (s16)y);
2798 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2800 v2s16 p2d = getSectorPos(sectordir);
2802 if(blockfile.size() != 4){
2803 throw InvalidFilenameException("Invalid block filename");
2806 int r = sscanf(blockfile.c_str(), "%4x", &y);
2808 throw InvalidFilenameException("Invalid block filename");
2809 return v3s16(p2d.X, y, p2d.Y);
2812 std::string ServerMap::getBlockFilename(v3s16 p)
2815 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2819 void ServerMap::save(ModifiedState save_level)
2821 DSTACK(__FUNCTION_NAME);
2822 if(m_map_saving_enabled == false)
2824 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2828 if(save_level == MOD_STATE_CLEAN)
2829 infostream<<"ServerMap: Saving whole map, this can take time."
2832 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2837 // Profile modified reasons
2838 Profiler modprofiler;
2840 u32 sector_meta_count = 0;
2841 u32 block_count = 0;
2842 u32 block_count_all = 0; // Number of blocks in memory
2844 // Don't do anything with sqlite unless something is really saved
2845 bool save_started = false;
2847 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2848 for(; i.atEnd() == false; i++)
2850 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2851 assert(sector->getId() == MAPSECTOR_SERVER);
2853 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2855 saveSectorMeta(sector);
2856 sector_meta_count++;
2858 core::list<MapBlock*> blocks;
2859 sector->getBlocks(blocks);
2860 core::list<MapBlock*>::Iterator j;
2862 for(j=blocks.begin(); j!=blocks.end(); j++)
2864 MapBlock *block = *j;
2868 if(block->getModified() >= save_level)
2873 save_started = true;
2876 modprofiler.add(block->getModifiedReason(), 1);
2881 /*infostream<<"ServerMap: Written block ("
2882 <<block->getPos().X<<","
2883 <<block->getPos().Y<<","
2884 <<block->getPos().Z<<")"
2893 Only print if something happened or saved whole map
2895 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2896 || block_count != 0)
2898 infostream<<"ServerMap: Written: "
2899 <<sector_meta_count<<" sector metadata files, "
2900 <<block_count<<" block files"
2901 <<", "<<block_count_all<<" blocks in memory."
2903 PrintInfo(infostream); // ServerMap/ClientMap:
2904 infostream<<"Blocks modified by: "<<std::endl;
2905 modprofiler.print(infostream);
2909 static s32 unsignedToSigned(s32 i, s32 max_positive)
2911 if(i < max_positive)
2914 return i - 2*max_positive;
2917 // modulo of a negative number does not work consistently in C
2918 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2922 return mod - ((-i) % mod);
2925 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2927 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2929 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2931 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2932 return v3s16(x,y,z);
2935 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2937 if(loadFromFolders()){
2938 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2939 <<"all blocks that are stored in flat files"<<std::endl;
2945 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2947 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2948 v3s16 p = getIntegerAsBlock(block_i);
2949 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2955 void ServerMap::saveMapMeta()
2957 DSTACK(__FUNCTION_NAME);
2959 /*infostream<<"ServerMap::saveMapMeta(): "
2963 createDirs(m_savedir);
2965 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2966 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2967 if(os.good() == false)
2969 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2970 <<"could not open"<<fullpath<<std::endl;
2971 throw FileNotGoodException("Cannot open chunk metadata");
2975 params.setU64("seed", m_seed);
2977 params.writeLines(os);
2979 os<<"[end_of_params]\n";
2981 m_map_metadata_changed = false;
2984 void ServerMap::loadMapMeta()
2986 DSTACK(__FUNCTION_NAME);
2988 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2991 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2992 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2993 if(is.good() == false)
2995 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2996 <<"could not open"<<fullpath<<std::endl;
2997 throw FileNotGoodException("Cannot open map metadata");
3005 throw SerializationError
3006 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3008 std::getline(is, line);
3009 std::string trimmedline = trim(line);
3010 if(trimmedline == "[end_of_params]")
3012 params.parseConfigLine(line);
3015 m_seed = params.getU64("seed");
3017 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3020 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3022 DSTACK(__FUNCTION_NAME);
3023 // Format used for writing
3024 u8 version = SER_FMT_VER_HIGHEST;
3026 v2s16 pos = sector->getPos();
3027 std::string dir = getSectorDir(pos);
3030 std::string fullpath = dir + DIR_DELIM + "meta";
3031 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3032 if(o.good() == false)
3033 throw FileNotGoodException("Cannot open sector metafile");
3035 sector->serialize(o, version);
3037 sector->differs_from_disk = false;
3040 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3042 DSTACK(__FUNCTION_NAME);
3044 v2s16 p2d = getSectorPos(sectordir);
3046 ServerMapSector *sector = NULL;
3048 std::string fullpath = sectordir + DIR_DELIM + "meta";
3049 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3050 if(is.good() == false)
3052 // If the directory exists anyway, it probably is in some old
3053 // format. Just go ahead and create the sector.
3054 if(fs::PathExists(sectordir))
3056 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3057 <<fullpath<<" doesn't exist but directory does."
3058 <<" Continuing with a sector with no metadata."
3060 sector = new ServerMapSector(this, p2d, m_gamedef);
3061 m_sectors.insert(p2d, sector);
3065 throw FileNotGoodException("Cannot open sector metafile");
3070 sector = ServerMapSector::deSerialize
3071 (is, this, p2d, m_sectors, m_gamedef);
3073 saveSectorMeta(sector);
3076 sector->differs_from_disk = false;
3081 bool ServerMap::loadSectorMeta(v2s16 p2d)
3083 DSTACK(__FUNCTION_NAME);
3085 MapSector *sector = NULL;
3087 // The directory layout we're going to load from.
3088 // 1 - original sectors/xxxxzzzz/
3089 // 2 - new sectors2/xxx/zzz/
3090 // If we load from anything but the latest structure, we will
3091 // immediately save to the new one, and remove the old.
3093 std::string sectordir1 = getSectorDir(p2d, 1);
3094 std::string sectordir;
3095 if(fs::PathExists(sectordir1))
3097 sectordir = sectordir1;
3102 sectordir = getSectorDir(p2d, 2);
3106 sector = loadSectorMeta(sectordir, loadlayout != 2);
3108 catch(InvalidFilenameException &e)
3112 catch(FileNotGoodException &e)
3116 catch(std::exception &e)
3125 bool ServerMap::loadSectorFull(v2s16 p2d)
3127 DSTACK(__FUNCTION_NAME);
3129 MapSector *sector = NULL;
3131 // The directory layout we're going to load from.
3132 // 1 - original sectors/xxxxzzzz/
3133 // 2 - new sectors2/xxx/zzz/
3134 // If we load from anything but the latest structure, we will
3135 // immediately save to the new one, and remove the old.
3137 std::string sectordir1 = getSectorDir(p2d, 1);
3138 std::string sectordir;
3139 if(fs::PathExists(sectordir1))
3141 sectordir = sectordir1;
3146 sectordir = getSectorDir(p2d, 2);
3150 sector = loadSectorMeta(sectordir, loadlayout != 2);
3152 catch(InvalidFilenameException &e)
3156 catch(FileNotGoodException &e)
3160 catch(std::exception &e)
3168 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3170 std::vector<fs::DirListNode>::iterator i2;
3171 for(i2=list2.begin(); i2!=list2.end(); i2++)
3177 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3179 catch(InvalidFilenameException &e)
3181 // This catches unknown crap in directory
3187 infostream<<"Sector converted to new layout - deleting "<<
3188 sectordir1<<std::endl;
3189 fs::RecursiveDelete(sectordir1);
3196 void ServerMap::beginSave() {
3198 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3199 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3202 void ServerMap::endSave() {
3204 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3205 infostream<<"WARNING: endSave() failed, map might not have saved.";
3208 void ServerMap::saveBlock(MapBlock *block)
3210 DSTACK(__FUNCTION_NAME);
3212 Dummy blocks are not written
3214 if(block->isDummy())
3216 /*v3s16 p = block->getPos();
3217 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3218 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3222 // Format used for writing
3223 u8 version = SER_FMT_VER_HIGHEST;
3225 v3s16 p3d = block->getPos();
3229 v2s16 p2d(p3d.X, p3d.Z);
3230 std::string sectordir = getSectorDir(p2d);
3232 createDirs(sectordir);
3234 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3235 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3236 if(o.good() == false)
3237 throw FileNotGoodException("Cannot open block data");
3240 [0] u8 serialization version
3246 std::ostringstream o(std::ios_base::binary);
3248 o.write((char*)&version, 1);
3251 block->serialize(o, version, true);
3253 // Write block to database
3255 std::string tmp = o.str();
3256 const char *bytes = tmp.c_str();
3258 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3259 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3260 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3261 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3262 int written = sqlite3_step(m_database_write);
3263 if(written != SQLITE_DONE)
3264 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3265 <<sqlite3_errmsg(m_database)<<std::endl;
3266 // Make ready for later reuse
3267 sqlite3_reset(m_database_write);
3269 // We just wrote it to the disk so clear modified flag
3270 block->resetModified();
3273 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3275 DSTACK(__FUNCTION_NAME);
3277 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3280 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3281 if(is.good() == false)
3282 throw FileNotGoodException("Cannot open block file");
3284 v3s16 p3d = getBlockPos(sectordir, blockfile);
3285 v2s16 p2d(p3d.X, p3d.Z);
3287 assert(sector->getPos() == p2d);
3289 u8 version = SER_FMT_VER_INVALID;
3290 is.read((char*)&version, 1);
3293 throw SerializationError("ServerMap::loadBlock(): Failed"
3294 " to read MapBlock version");
3296 /*u32 block_size = MapBlock::serializedLength(version);
3297 SharedBuffer<u8> data(block_size);
3298 is.read((char*)*data, block_size);*/
3300 // This will always return a sector because we're the server
3301 //MapSector *sector = emergeSector(p2d);
3303 MapBlock *block = NULL;
3304 bool created_new = false;
3305 block = sector->getBlockNoCreateNoEx(p3d.Y);
3308 block = sector->createBlankBlockNoInsert(p3d.Y);
3313 block->deSerialize(is, version, true);
3315 // If it's a new block, insert it to the map
3317 sector->insertBlock(block);
3320 Save blocks loaded in old format in new format
3323 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3327 // Should be in database now, so delete the old file
3328 fs::RecursiveDelete(fullpath);
3331 // We just loaded it from the disk, so it's up-to-date.
3332 block->resetModified();
3335 catch(SerializationError &e)
3337 infostream<<"WARNING: Invalid block data on disk "
3338 <<"fullpath="<<fullpath
3339 <<" (SerializationError). "
3340 <<"what()="<<e.what()
3342 //" Ignoring. A new one will be generated.
3345 // TODO: Backup file; name is in fullpath.
3349 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3351 DSTACK(__FUNCTION_NAME);
3354 std::istringstream is(*blob, std::ios_base::binary);
3356 u8 version = SER_FMT_VER_INVALID;
3357 is.read((char*)&version, 1);
3360 throw SerializationError("ServerMap::loadBlock(): Failed"
3361 " to read MapBlock version");
3363 /*u32 block_size = MapBlock::serializedLength(version);
3364 SharedBuffer<u8> data(block_size);
3365 is.read((char*)*data, block_size);*/
3367 // This will always return a sector because we're the server
3368 //MapSector *sector = emergeSector(p2d);
3370 MapBlock *block = NULL;
3371 bool created_new = false;
3372 block = sector->getBlockNoCreateNoEx(p3d.Y);
3375 block = sector->createBlankBlockNoInsert(p3d.Y);
3380 block->deSerialize(is, version, true);
3382 // If it's a new block, insert it to the map
3384 sector->insertBlock(block);
3387 Save blocks loaded in old format in new format
3390 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3391 // Only save if asked to; no need to update version
3395 // We just loaded it from, so it's up-to-date.
3396 block->resetModified();
3399 catch(SerializationError &e)
3401 infostream<<"WARNING: Invalid block data in database "
3402 <<" (SerializationError). "
3403 <<"what()="<<e.what()
3405 //" Ignoring. A new one will be generated.
3408 // TODO: Copy to a backup database.
3412 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3414 DSTACK(__FUNCTION_NAME);
3416 v2s16 p2d(blockpos.X, blockpos.Z);
3418 if(!loadFromFolders()) {
3421 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3422 infostream<<"WARNING: Could not bind block position for load: "
3423 <<sqlite3_errmsg(m_database)<<std::endl;
3424 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3426 Make sure sector is loaded
3428 MapSector *sector = createSector(p2d);
3433 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3434 size_t len = sqlite3_column_bytes(m_database_read, 0);
3436 std::string datastr(data, len);
3438 loadBlock(&datastr, blockpos, sector, false);
3440 sqlite3_step(m_database_read);
3441 // We should never get more than 1 row, so ok to reset
3442 sqlite3_reset(m_database_read);
3444 return getBlockNoCreateNoEx(blockpos);
3446 sqlite3_reset(m_database_read);
3448 // Not found in database, try the files
3451 // The directory layout we're going to load from.
3452 // 1 - original sectors/xxxxzzzz/
3453 // 2 - new sectors2/xxx/zzz/
3454 // If we load from anything but the latest structure, we will
3455 // immediately save to the new one, and remove the old.
3457 std::string sectordir1 = getSectorDir(p2d, 1);
3458 std::string sectordir;
3459 if(fs::PathExists(sectordir1))
3461 sectordir = sectordir1;
3466 sectordir = getSectorDir(p2d, 2);
3470 Make sure sector is loaded
3472 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3476 sector = loadSectorMeta(sectordir, loadlayout != 2);
3478 catch(InvalidFilenameException &e)
3482 catch(FileNotGoodException &e)
3486 catch(std::exception &e)
3493 Make sure file exists
3496 std::string blockfilename = getBlockFilename(blockpos);
3497 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3501 Load block and save it to the database
3503 loadBlock(sectordir, blockfilename, sector, true);
3504 return getBlockNoCreateNoEx(blockpos);
3507 void ServerMap::PrintInfo(std::ostream &out)
3516 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3521 MapVoxelManipulator::~MapVoxelManipulator()
3523 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3527 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3529 TimeTaker timer1("emerge", &emerge_time);
3531 // Units of these are MapBlocks
3532 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3533 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3535 VoxelArea block_area_nodes
3536 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3538 addArea(block_area_nodes);
3540 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3541 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3542 for(s32 x=p_min.X; x<=p_max.X; x++)
3545 core::map<v3s16, bool>::Node *n;
3546 n = m_loaded_blocks.find(p);
3550 bool block_data_inexistent = false;
3553 TimeTaker timer1("emerge load", &emerge_load_time);
3555 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3556 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3558 a.print(infostream);
3559 infostream<<std::endl;*/
3561 MapBlock *block = m_map->getBlockNoCreate(p);
3562 if(block->isDummy())
3563 block_data_inexistent = true;
3565 block->copyTo(*this);
3567 catch(InvalidPositionException &e)
3569 block_data_inexistent = true;
3572 if(block_data_inexistent)
3574 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3575 // Fill with VOXELFLAG_INEXISTENT
3576 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3577 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3579 s32 i = m_area.index(a.MinEdge.X,y,z);
3580 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3584 m_loaded_blocks.insert(p, !block_data_inexistent);
3587 //infostream<<"emerge done"<<std::endl;
3591 SUGG: Add an option to only update eg. water and air nodes.
3592 This will make it interfere less with important stuff if
3595 void MapVoxelManipulator::blitBack
3596 (core::map<v3s16, MapBlock*> & modified_blocks)
3598 if(m_area.getExtent() == v3s16(0,0,0))
3601 //TimeTaker timer1("blitBack");
3603 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3604 <<m_loaded_blocks.size()<<std::endl;*/
3607 Initialize block cache
3609 v3s16 blockpos_last;
3610 MapBlock *block = NULL;
3611 bool block_checked_in_modified = false;
3613 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3614 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3615 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3619 u8 f = m_flags[m_area.index(p)];
3620 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3623 MapNode &n = m_data[m_area.index(p)];
3625 v3s16 blockpos = getNodeBlockPos(p);
3630 if(block == NULL || blockpos != blockpos_last){
3631 block = m_map->getBlockNoCreate(blockpos);
3632 blockpos_last = blockpos;
3633 block_checked_in_modified = false;
3636 // Calculate relative position in block
3637 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3639 // Don't continue if nothing has changed here
3640 if(block->getNode(relpos) == n)
3643 //m_map->setNode(m_area.MinEdge + p, n);
3644 block->setNode(relpos, n);
3647 Make sure block is in modified_blocks
3649 if(block_checked_in_modified == false)
3651 modified_blocks[blockpos] = block;
3652 block_checked_in_modified = true;
3655 catch(InvalidPositionException &e)
3661 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3662 MapVoxelManipulator(map),
3663 m_create_area(false)
3667 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3671 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3673 // Just create the area so that it can be pointed to
3674 VoxelManipulator::emerge(a, caller_id);
3677 void ManualMapVoxelManipulator::initialEmerge(
3678 v3s16 blockpos_min, v3s16 blockpos_max)
3680 TimeTaker timer1("initialEmerge", &emerge_time);
3682 // Units of these are MapBlocks
3683 v3s16 p_min = blockpos_min;
3684 v3s16 p_max = blockpos_max;
3686 VoxelArea block_area_nodes
3687 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3689 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3692 infostream<<"initialEmerge: area: ";
3693 block_area_nodes.print(infostream);
3694 infostream<<" ("<<size_MB<<"MB)";
3695 infostream<<std::endl;
3698 addArea(block_area_nodes);
3700 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3701 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3702 for(s32 x=p_min.X; x<=p_max.X; x++)
3705 core::map<v3s16, bool>::Node *n;
3706 n = m_loaded_blocks.find(p);
3710 bool block_data_inexistent = false;
3713 TimeTaker timer1("emerge load", &emerge_load_time);
3715 MapBlock *block = m_map->getBlockNoCreate(p);
3716 if(block->isDummy())
3717 block_data_inexistent = true;
3719 block->copyTo(*this);
3721 catch(InvalidPositionException &e)
3723 block_data_inexistent = true;
3726 if(block_data_inexistent)
3729 Mark area inexistent
3731 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3732 // Fill with VOXELFLAG_INEXISTENT
3733 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3734 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3736 s32 i = m_area.index(a.MinEdge.X,y,z);
3737 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3741 m_loaded_blocks.insert(p, !block_data_inexistent);
3745 void ManualMapVoxelManipulator::blitBackAll(
3746 core::map<v3s16, MapBlock*> * modified_blocks)
3748 if(m_area.getExtent() == v3s16(0,0,0))
3752 Copy data of all blocks
3754 for(core::map<v3s16, bool>::Iterator
3755 i = m_loaded_blocks.getIterator();
3756 i.atEnd() == false; i++)
3758 v3s16 p = i.getNode()->getKey();
3759 bool existed = i.getNode()->getValue();
3760 if(existed == false)
3762 // The Great Bug was found using this
3763 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3764 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3768 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3771 infostream<<"WARNING: "<<__FUNCTION_NAME
3772 <<": got NULL block "
3773 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3778 block->copyFrom(*this);
3781 modified_blocks->insert(p, block);