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;
694 int num_bottom_invalid = 0;
697 TimeTaker t("first stuff");
699 core::map<v3s16, MapBlock*>::Iterator i;
700 i = a_blocks.getIterator();
701 for(; i.atEnd() == false; i++)
703 MapBlock *block = i.getNode()->getValue();
707 // Don't bother with dummy blocks.
711 v3s16 pos = block->getPos();
712 v3s16 posnodes = block->getPosRelative();
713 modified_blocks.insert(pos, block);
715 blocks_to_update.insert(pos, block);
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(p);
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(p, n);
732 // If node sources light, add to list
733 u8 source = nodemgr->get(n).light_source;
735 light_sources[p + posnodes] = true;
737 // Collect borders for unlighting
738 if((x==0 || x == MAP_BLOCKSIZE-1
739 || y==0 || y == MAP_BLOCKSIZE-1
740 || z==0 || z == MAP_BLOCKSIZE-1)
743 v3s16 p_map = p + posnodes;
744 unlight_from.insert(p_map, oldlight);
747 catch(InvalidPositionException &e)
750 This would happen when dealing with a
754 infostream<<"updateLighting(): InvalidPositionException"
759 if(bank == LIGHTBANK_DAY)
761 bool bottom_valid = block->propagateSunlight(light_sources);
764 num_bottom_invalid++;
766 // If bottom is valid, we're done.
770 else if(bank == LIGHTBANK_NIGHT)
772 // For night lighting, sunlight is not propagated
777 // Invalid lighting bank
781 /*infostream<<"Bottom for sunlight-propagated block ("
782 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
785 // Bottom sunlight is not valid; get the block and loop to it
789 block = getBlockNoCreate(pos);
791 catch(InvalidPositionException &e)
801 infostream<<"num_bottom_invalid="<<num_bottom_invalid<<std::endl;
804 Enable this to disable proper lighting for speeding up map
805 generation for testing or whatever
808 //if(g_settings->get(""))
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
816 block->setLightingExpired(false);
824 //TimeTaker timer("unspreadLight");
825 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 infostream<<"unspreadLight modified "<<diff<<std::endl;
836 //TimeTaker timer("spreadLight");
837 spreadLight(bank, light_sources, modified_blocks);
842 u32 diff = modified_blocks.size() - count_was;
843 count_was = modified_blocks.size();
844 infostream<<"spreadLight modified "<<diff<<std::endl;
850 //MapVoxelManipulator vmanip(this);
852 // Make a manual voxel manipulator and load all the blocks
853 // that touch the requested blocks
854 ManualMapVoxelManipulator vmanip(this);
857 //TimeTaker timer("initialEmerge");
859 core::map<v3s16, MapBlock*>::Iterator i;
860 i = blocks_to_update.getIterator();
861 for(; i.atEnd() == false; i++)
863 MapBlock *block = i.getNode()->getValue();
864 v3s16 p = block->getPos();
866 // Add all surrounding blocks
867 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
870 Add all surrounding blocks that have up-to-date lighting
871 NOTE: This doesn't quite do the job (not everything
872 appropriate is lighted)
874 /*for(s16 z=-1; z<=1; z++)
875 for(s16 y=-1; y<=1; y++)
876 for(s16 x=-1; x<=1; x++)
878 v3s16 p2 = p + v3s16(x,y,z);
879 MapBlock *block = getBlockNoCreateNoEx(p2);
884 if(block->getLightingExpired())
886 vmanip.initialEmerge(p2, p2);
889 // Lighting of block will be updated completely
890 block->setLightingExpired(false);
895 //TimeTaker timer("unSpreadLight");
896 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
899 //TimeTaker timer("spreadLight");
900 vmanip.spreadLight(bank, light_sources, nodemgr);
903 //TimeTaker timer("blitBack");
904 vmanip.blitBack(modified_blocks);
906 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
911 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
914 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
915 core::map<v3s16, MapBlock*> & modified_blocks)
917 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
918 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
921 Update information about whether day and night light differ
923 for(core::map<v3s16, MapBlock*>::Iterator
924 i = modified_blocks.getIterator();
925 i.atEnd() == false; i++)
927 MapBlock *block = i.getNode()->getValue();
928 block->updateDayNightDiff();
934 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
935 core::map<v3s16, MapBlock*> &modified_blocks)
937 INodeDefManager *nodemgr = m_gamedef->ndef();
940 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
941 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
944 From this node to nodes underneath:
945 If lighting is sunlight (1.0), unlight neighbours and
950 v3s16 toppos = p + v3s16(0,1,0);
951 v3s16 bottompos = p + v3s16(0,-1,0);
953 bool node_under_sunlight = true;
954 core::map<v3s16, bool> light_sources;
957 If there is a node at top and it doesn't have sunlight,
958 there has not been any sunlight going down.
960 Otherwise there probably is.
963 MapNode topnode = getNode(toppos);
965 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
966 node_under_sunlight = false;
968 catch(InvalidPositionException &e)
973 Remove all light that has come out of this node
976 enum LightBank banks[] =
981 for(s32 i=0; i<2; i++)
983 enum LightBank bank = banks[i];
985 u8 lightwas = getNode(p).getLight(bank, nodemgr);
987 // Add the block of the added node to modified_blocks
988 v3s16 blockpos = getNodeBlockPos(p);
989 MapBlock * block = getBlockNoCreate(blockpos);
990 assert(block != NULL);
991 modified_blocks.insert(blockpos, block);
993 assert(isValidPosition(p));
995 // Unlight neighbours of node.
996 // This means setting light of all consequent dimmer nodes
998 // This also collects the nodes at the border which will spread
999 // light again into this.
1000 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1002 n.setLight(bank, 0, nodemgr);
1006 If node lets sunlight through and is under sunlight, it has
1009 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1011 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1015 Set the node on the map
1024 std::string metadata_name = nodemgr->get(n).metadata_name;
1025 if(metadata_name != ""){
1026 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1028 errorstream<<"Failed to create node metadata \""
1029 <<metadata_name<<"\""<<std::endl;
1031 setNodeMetadata(p, meta);
1036 If node is under sunlight and doesn't let sunlight through,
1037 take all sunlighted nodes under it and clear light from them
1038 and from where the light has been spread.
1039 TODO: This could be optimized by mass-unlighting instead
1042 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1046 //m_dout<<DTIME<<"y="<<y<<std::endl;
1047 v3s16 n2pos(p.X, y, p.Z);
1051 n2 = getNode(n2pos);
1053 catch(InvalidPositionException &e)
1058 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1060 unLightNeighbors(LIGHTBANK_DAY,
1061 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1062 light_sources, modified_blocks);
1063 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1071 for(s32 i=0; i<2; i++)
1073 enum LightBank bank = banks[i];
1076 Spread light from all nodes that might be capable of doing so
1078 spreadLight(bank, light_sources, modified_blocks);
1082 Update information about whether day and night light differ
1084 for(core::map<v3s16, MapBlock*>::Iterator
1085 i = modified_blocks.getIterator();
1086 i.atEnd() == false; i++)
1088 MapBlock *block = i.getNode()->getValue();
1089 block->updateDayNightDiff();
1093 Add neighboring liquid nodes and the node itself if it is
1094 liquid (=water node was added) to transform queue.
1097 v3s16(0,0,0), // self
1098 v3s16(0,0,1), // back
1099 v3s16(0,1,0), // top
1100 v3s16(1,0,0), // right
1101 v3s16(0,0,-1), // front
1102 v3s16(0,-1,0), // bottom
1103 v3s16(-1,0,0), // left
1105 for(u16 i=0; i<7; i++)
1110 v3s16 p2 = p + dirs[i];
1112 MapNode n2 = getNode(p2);
1113 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1115 m_transforming_liquid.push_back(p2);
1118 }catch(InvalidPositionException &e)
1126 void Map::removeNodeAndUpdate(v3s16 p,
1127 core::map<v3s16, MapBlock*> &modified_blocks)
1129 INodeDefManager *nodemgr = m_gamedef->ndef();
1131 /*PrintInfo(m_dout);
1132 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1133 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1135 bool node_under_sunlight = true;
1137 v3s16 toppos = p + v3s16(0,1,0);
1139 // Node will be replaced with this
1140 content_t replace_material = CONTENT_AIR;
1143 If there is a node at top and it doesn't have sunlight,
1144 there will be no sunlight going down.
1147 MapNode topnode = getNode(toppos);
1149 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1150 node_under_sunlight = false;
1152 catch(InvalidPositionException &e)
1156 core::map<v3s16, bool> light_sources;
1158 enum LightBank banks[] =
1163 for(s32 i=0; i<2; i++)
1165 enum LightBank bank = banks[i];
1168 Unlight neighbors (in case the node is a light source)
1170 unLightNeighbors(bank, p,
1171 getNode(p).getLight(bank, nodemgr),
1172 light_sources, modified_blocks);
1176 Remove node metadata
1179 removeNodeMetadata(p);
1183 This also clears the lighting.
1187 n.setContent(replace_material);
1190 for(s32 i=0; i<2; i++)
1192 enum LightBank bank = banks[i];
1195 Recalculate lighting
1197 spreadLight(bank, light_sources, modified_blocks);
1200 // Add the block of the removed node to modified_blocks
1201 v3s16 blockpos = getNodeBlockPos(p);
1202 MapBlock * block = getBlockNoCreate(blockpos);
1203 assert(block != NULL);
1204 modified_blocks.insert(blockpos, block);
1207 If the removed node was under sunlight, propagate the
1208 sunlight down from it and then light all neighbors
1209 of the propagated blocks.
1211 if(node_under_sunlight)
1213 s16 ybottom = propagateSunlight(p, modified_blocks);
1214 /*m_dout<<DTIME<<"Node was under sunlight. "
1215 "Propagating sunlight";
1216 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1218 for(; y >= ybottom; y--)
1220 v3s16 p2(p.X, y, p.Z);
1221 /*m_dout<<DTIME<<"lighting neighbors of node ("
1222 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1224 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1229 // Set the lighting of this node to 0
1230 // TODO: Is this needed? Lighting is cleared up there already.
1232 MapNode n = getNode(p);
1233 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1236 catch(InvalidPositionException &e)
1242 for(s32 i=0; i<2; i++)
1244 enum LightBank bank = banks[i];
1246 // Get the brightest neighbour node and propagate light from it
1247 v3s16 n2p = getBrightestNeighbour(bank, p);
1249 MapNode n2 = getNode(n2p);
1250 lightNeighbors(bank, n2p, modified_blocks);
1252 catch(InvalidPositionException &e)
1258 Update information about whether day and night light differ
1260 for(core::map<v3s16, MapBlock*>::Iterator
1261 i = modified_blocks.getIterator();
1262 i.atEnd() == false; i++)
1264 MapBlock *block = i.getNode()->getValue();
1265 block->updateDayNightDiff();
1269 Add neighboring liquid nodes and this node to transform queue.
1270 (it's vital for the node itself to get updated last.)
1273 v3s16(0,0,1), // back
1274 v3s16(0,1,0), // top
1275 v3s16(1,0,0), // right
1276 v3s16(0,0,-1), // front
1277 v3s16(0,-1,0), // bottom
1278 v3s16(-1,0,0), // left
1279 v3s16(0,0,0), // self
1281 for(u16 i=0; i<7; i++)
1286 v3s16 p2 = p + dirs[i];
1288 MapNode n2 = getNode(p2);
1289 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1291 m_transforming_liquid.push_back(p2);
1294 }catch(InvalidPositionException &e)
1300 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1303 event.type = MEET_ADDNODE;
1307 bool succeeded = true;
1309 core::map<v3s16, MapBlock*> modified_blocks;
1310 addNodeAndUpdate(p, n, modified_blocks);
1312 // Copy modified_blocks to event
1313 for(core::map<v3s16, MapBlock*>::Iterator
1314 i = modified_blocks.getIterator();
1315 i.atEnd()==false; i++)
1317 event.modified_blocks.insert(i.getNode()->getKey(), false);
1320 catch(InvalidPositionException &e){
1324 dispatchEvent(&event);
1329 bool Map::removeNodeWithEvent(v3s16 p)
1332 event.type = MEET_REMOVENODE;
1335 bool succeeded = true;
1337 core::map<v3s16, MapBlock*> modified_blocks;
1338 removeNodeAndUpdate(p, modified_blocks);
1340 // Copy modified_blocks to event
1341 for(core::map<v3s16, MapBlock*>::Iterator
1342 i = modified_blocks.getIterator();
1343 i.atEnd()==false; i++)
1345 event.modified_blocks.insert(i.getNode()->getKey(), false);
1348 catch(InvalidPositionException &e){
1352 dispatchEvent(&event);
1357 bool Map::dayNightDiffed(v3s16 blockpos)
1360 v3s16 p = blockpos + v3s16(0,0,0);
1361 MapBlock *b = getBlockNoCreate(p);
1362 if(b->dayNightDiffed())
1365 catch(InvalidPositionException &e){}
1368 v3s16 p = blockpos + v3s16(-1,0,0);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->dayNightDiffed())
1373 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(0,-1,0);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(0,0,-1);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->dayNightDiffed())
1387 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(1,0,0);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->dayNightDiffed())
1395 catch(InvalidPositionException &e){}
1397 v3s16 p = blockpos + v3s16(0,1,0);
1398 MapBlock *b = getBlockNoCreate(p);
1399 if(b->dayNightDiffed())
1402 catch(InvalidPositionException &e){}
1404 v3s16 p = blockpos + v3s16(0,0,1);
1405 MapBlock *b = getBlockNoCreate(p);
1406 if(b->dayNightDiffed())
1409 catch(InvalidPositionException &e){}
1415 Updates usage timers
1417 void Map::timerUpdate(float dtime, float unload_timeout,
1418 core::list<v3s16> *unloaded_blocks)
1420 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1422 // Profile modified reasons
1423 Profiler modprofiler;
1425 core::list<v2s16> sector_deletion_queue;
1426 u32 deleted_blocks_count = 0;
1427 u32 saved_blocks_count = 0;
1428 u32 block_count_all = 0;
1430 core::map<v2s16, MapSector*>::Iterator si;
1433 si = m_sectors.getIterator();
1434 for(; si.atEnd() == false; si++)
1436 MapSector *sector = si.getNode()->getValue();
1438 bool all_blocks_deleted = true;
1440 core::list<MapBlock*> blocks;
1441 sector->getBlocks(blocks);
1443 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1444 i != blocks.end(); i++)
1446 MapBlock *block = (*i);
1448 block->incrementUsageTimer(dtime);
1450 if(block->getUsageTimer() > unload_timeout)
1452 v3s16 p = block->getPos();
1455 if(block->getModified() != MOD_STATE_CLEAN
1456 && save_before_unloading)
1458 modprofiler.add(block->getModifiedReason(), 1);
1460 saved_blocks_count++;
1463 // Delete from memory
1464 sector->deleteBlock(block);
1467 unloaded_blocks->push_back(p);
1469 deleted_blocks_count++;
1473 all_blocks_deleted = false;
1478 if(all_blocks_deleted)
1480 sector_deletion_queue.push_back(si.getNode()->getKey());
1485 // Finally delete the empty sectors
1486 deleteSectors(sector_deletion_queue);
1488 if(deleted_blocks_count != 0)
1490 PrintInfo(infostream); // ServerMap/ClientMap:
1491 infostream<<"Unloaded "<<deleted_blocks_count
1492 <<" blocks from memory";
1493 if(save_before_unloading)
1494 infostream<<", of which "<<saved_blocks_count<<" were written";
1495 infostream<<", "<<block_count_all<<" blocks in memory";
1496 infostream<<"."<<std::endl;
1497 if(saved_blocks_count != 0){
1498 PrintInfo(infostream); // ServerMap/ClientMap:
1499 infostream<<"Blocks modified by: "<<std::endl;
1500 modprofiler.print(infostream);
1505 void Map::deleteSectors(core::list<v2s16> &list)
1507 core::list<v2s16>::Iterator j;
1508 for(j=list.begin(); j!=list.end(); j++)
1510 MapSector *sector = m_sectors[*j];
1511 // If sector is in sector cache, remove it from there
1512 if(m_sector_cache == sector)
1513 m_sector_cache = NULL;
1514 // Remove from map and delete
1515 m_sectors.remove(*j);
1521 void Map::unloadUnusedData(float timeout,
1522 core::list<v3s16> *deleted_blocks)
1524 core::list<v2s16> sector_deletion_queue;
1525 u32 deleted_blocks_count = 0;
1526 u32 saved_blocks_count = 0;
1528 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1529 for(; si.atEnd() == false; si++)
1531 MapSector *sector = si.getNode()->getValue();
1533 bool all_blocks_deleted = true;
1535 core::list<MapBlock*> blocks;
1536 sector->getBlocks(blocks);
1537 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1538 i != blocks.end(); i++)
1540 MapBlock *block = (*i);
1542 if(block->getUsageTimer() > timeout)
1545 if(block->getModified() != MOD_STATE_CLEAN)
1548 saved_blocks_count++;
1550 // Delete from memory
1551 sector->deleteBlock(block);
1552 deleted_blocks_count++;
1556 all_blocks_deleted = false;
1560 if(all_blocks_deleted)
1562 sector_deletion_queue.push_back(si.getNode()->getKey());
1566 deleteSectors(sector_deletion_queue);
1568 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1569 <<", of which "<<saved_blocks_count<<" were wr."
1572 //return sector_deletion_queue.getSize();
1573 //return deleted_blocks_count;
1577 void Map::PrintInfo(std::ostream &out)
1582 #define WATER_DROP_BOOST 4
1586 NEIGHBOR_SAME_LEVEL,
1589 struct NodeNeighbor {
1595 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1597 INodeDefManager *nodemgr = m_gamedef->ndef();
1599 DSTACK(__FUNCTION_NAME);
1600 //TimeTaker timer("transformLiquids()");
1603 u32 initial_size = m_transforming_liquid.size();
1605 /*if(initial_size != 0)
1606 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1608 // list of nodes that due to viscosity have not reached their max level height
1609 UniqueQueue<v3s16> must_reflow;
1611 // List of MapBlocks that will require a lighting update (due to lava)
1612 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1614 while(m_transforming_liquid.size() != 0)
1616 // This should be done here so that it is done when continue is used
1617 if(loopcount >= initial_size * 3)
1622 Get a queued transforming liquid node
1624 v3s16 p0 = m_transforming_liquid.pop_front();
1626 MapNode n0 = getNodeNoEx(p0);
1629 Collect information about current node
1631 s8 liquid_level = -1;
1632 u8 liquid_kind = CONTENT_IGNORE;
1633 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1634 switch (liquid_type) {
1636 liquid_level = LIQUID_LEVEL_SOURCE;
1637 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1639 case LIQUID_FLOWING:
1640 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1641 liquid_kind = n0.getContent();
1644 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1645 // continue with the next node.
1646 if (n0.getContent() != CONTENT_AIR)
1648 liquid_kind = CONTENT_AIR;
1653 Collect information about the environment
1655 const v3s16 *dirs = g_6dirs;
1656 NodeNeighbor sources[6]; // surrounding sources
1657 int num_sources = 0;
1658 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1660 NodeNeighbor airs[6]; // surrounding air
1662 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1663 int num_neutrals = 0;
1664 bool flowing_down = false;
1665 for (u16 i = 0; i < 6; i++) {
1666 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1669 nt = NEIGHBOR_UPPER;
1672 nt = NEIGHBOR_LOWER;
1675 v3s16 npos = p0 + dirs[i];
1676 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1677 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1679 if (nb.n.getContent() == CONTENT_AIR) {
1680 airs[num_airs++] = nb;
1681 // if the current node is a water source the neighbor
1682 // should be enqueded for transformation regardless of whether the
1683 // current node changes or not.
1684 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1685 m_transforming_liquid.push_back(npos);
1686 // if the current node happens to be a flowing node, it will start to flow down here.
1687 if (nb.t == NEIGHBOR_LOWER) {
1688 flowing_down = true;
1691 neutrals[num_neutrals++] = nb;
1695 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1696 if (liquid_kind == CONTENT_AIR)
1697 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1698 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1699 neutrals[num_neutrals++] = nb;
1701 // Do not count bottom source, it will screw things up
1703 sources[num_sources++] = nb;
1706 case LIQUID_FLOWING:
1707 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1708 if (liquid_kind == CONTENT_AIR)
1709 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1710 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1711 neutrals[num_neutrals++] = nb;
1713 flows[num_flows++] = nb;
1714 if (nb.t == NEIGHBOR_LOWER)
1715 flowing_down = true;
1722 decide on the type (and possibly level) of the current node
1724 content_t new_node_content;
1725 s8 new_node_level = -1;
1726 s8 max_node_level = -1;
1727 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1728 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1729 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1730 // it's perfectly safe to use liquid_kind here to determine the new node content.
1731 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1732 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1733 // liquid_kind is set properly, see above
1734 new_node_content = liquid_kind;
1735 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1737 // no surrounding sources, so get the maximum level that can flow into this node
1738 for (u16 i = 0; i < num_flows; i++) {
1739 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1740 switch (flows[i].t) {
1741 case NEIGHBOR_UPPER:
1742 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1743 max_node_level = LIQUID_LEVEL_MAX;
1744 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1745 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1746 } else if (nb_liquid_level > max_node_level)
1747 max_node_level = nb_liquid_level;
1749 case NEIGHBOR_LOWER:
1751 case NEIGHBOR_SAME_LEVEL:
1752 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1753 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1754 max_node_level = nb_liquid_level - 1;
1760 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1761 if (viscosity > 1 && max_node_level != liquid_level) {
1762 // amount to gain, limited by viscosity
1763 // must be at least 1 in absolute value
1764 s8 level_inc = max_node_level - liquid_level;
1765 if (level_inc < -viscosity || level_inc > viscosity)
1766 new_node_level = liquid_level + level_inc/viscosity;
1767 else if (level_inc < 0)
1768 new_node_level = liquid_level - 1;
1769 else if (level_inc > 0)
1770 new_node_level = liquid_level + 1;
1771 if (new_node_level != max_node_level)
1772 must_reflow.push_back(p0);
1774 new_node_level = max_node_level;
1776 if (new_node_level >= 0)
1777 new_node_content = liquid_kind;
1779 new_node_content = CONTENT_AIR;
1784 check if anything has changed. if not, just continue with the next node.
1786 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1787 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1788 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1794 update the current node
1796 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1797 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1798 // set level to last 3 bits, flowing down bit to 4th bit
1799 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1801 // set the liquid level and flow bit to 0
1802 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1804 n0.setContent(new_node_content);
1806 v3s16 blockpos = getNodeBlockPos(p0);
1807 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1809 modified_blocks.insert(blockpos, block);
1810 // If node emits light, MapBlock requires lighting update
1811 if(nodemgr->get(n0).light_source != 0)
1812 lighting_modified_blocks[block->getPos()] = block;
1816 enqueue neighbors for update if neccessary
1818 switch (nodemgr->get(n0.getContent()).liquid_type) {
1820 case LIQUID_FLOWING:
1821 // make sure source flows into all neighboring nodes
1822 for (u16 i = 0; i < num_flows; i++)
1823 if (flows[i].t != NEIGHBOR_UPPER)
1824 m_transforming_liquid.push_back(flows[i].p);
1825 for (u16 i = 0; i < num_airs; i++)
1826 if (airs[i].t != NEIGHBOR_UPPER)
1827 m_transforming_liquid.push_back(airs[i].p);
1830 // this flow has turned to air; neighboring flows might need to do the same
1831 for (u16 i = 0; i < num_flows; i++)
1832 m_transforming_liquid.push_back(flows[i].p);
1836 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1837 while (must_reflow.size() > 0)
1838 m_transforming_liquid.push_back(must_reflow.pop_front());
1839 updateLighting(lighting_modified_blocks, modified_blocks);
1842 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1844 v3s16 blockpos = getNodeBlockPos(p);
1845 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1846 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1848 infostream<<"Map::getNodeMetadata(): Need to emerge "
1849 <<PP(blockpos)<<std::endl;
1850 block = emergeBlock(blockpos, false);
1854 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1858 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1862 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1864 v3s16 blockpos = getNodeBlockPos(p);
1865 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1866 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1868 infostream<<"Map::setNodeMetadata(): Need to emerge "
1869 <<PP(blockpos)<<std::endl;
1870 block = emergeBlock(blockpos, false);
1874 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1878 block->m_node_metadata->set(p_rel, meta);
1881 void Map::removeNodeMetadata(v3s16 p)
1883 v3s16 blockpos = getNodeBlockPos(p);
1884 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1885 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1888 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1892 block->m_node_metadata->remove(p_rel);
1895 void Map::nodeMetadataStep(float dtime,
1896 core::map<v3s16, MapBlock*> &changed_blocks)
1900 Currently there is no way to ensure that all the necessary
1901 blocks are loaded when this is run. (They might get unloaded)
1902 NOTE: ^- Actually, that might not be so. In a quick test it
1903 reloaded a block with a furnace when I walked back to it from
1906 core::map<v2s16, MapSector*>::Iterator si;
1907 si = m_sectors.getIterator();
1908 for(; si.atEnd() == false; si++)
1910 MapSector *sector = si.getNode()->getValue();
1911 core::list< MapBlock * > sectorblocks;
1912 sector->getBlocks(sectorblocks);
1913 core::list< MapBlock * >::Iterator i;
1914 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1916 MapBlock *block = *i;
1917 bool changed = block->m_node_metadata->step(dtime);
1919 changed_blocks[block->getPos()] = block;
1928 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1929 Map(dout_server, gamedef),
1931 m_map_metadata_changed(true),
1933 m_database_read(NULL),
1934 m_database_write(NULL)
1936 verbosestream<<__FUNCTION_NAME<<std::endl;
1938 //m_chunksize = 8; // Takes a few seconds
1940 if (g_settings->get("fixed_map_seed").empty())
1942 m_seed = (((u64)(myrand()%0xffff)<<0)
1943 + ((u64)(myrand()%0xffff)<<16)
1944 + ((u64)(myrand()%0xffff)<<32)
1945 + ((u64)(myrand()%0xffff)<<48));
1949 m_seed = g_settings->getU64("fixed_map_seed");
1953 Experimental and debug stuff
1960 Try to load map; if not found, create a new one.
1963 m_savedir = savedir;
1964 m_map_saving_enabled = false;
1968 // If directory exists, check contents and load if possible
1969 if(fs::PathExists(m_savedir))
1971 // If directory is empty, it is safe to save into it.
1972 if(fs::GetDirListing(m_savedir).size() == 0)
1974 infostream<<"ServerMap: Empty save directory is valid."
1976 m_map_saving_enabled = true;
1981 // Load map metadata (seed, chunksize)
1984 catch(FileNotGoodException &e){
1985 infostream<<"WARNING: Could not load map metadata"
1986 //<<" Disabling chunk-based generator."
1991 infostream<<"ServerMap: Successfully loaded map "
1992 <<"metadata from "<<savedir
1993 <<", assuming valid save directory."
1994 <<" seed="<<m_seed<<"."
1997 m_map_saving_enabled = true;
1998 // Map loaded, not creating new one
2002 // If directory doesn't exist, it is safe to save to it
2004 m_map_saving_enabled = true;
2007 catch(std::exception &e)
2009 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2010 <<", exception: "<<e.what()<<std::endl;
2011 infostream<<"Please remove the map or fix it."<<std::endl;
2012 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2015 infostream<<"Initializing new map."<<std::endl;
2017 // Create zero sector
2018 emergeSector(v2s16(0,0));
2020 // Initially write whole map
2021 save(MOD_STATE_CLEAN);
2024 ServerMap::~ServerMap()
2026 verbosestream<<__FUNCTION_NAME<<std::endl;
2030 if(m_map_saving_enabled)
2032 // Save only changed parts
2033 save(MOD_STATE_WRITE_AT_UNLOAD);
2034 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2038 infostream<<"ServerMap: Map not saved"<<std::endl;
2041 catch(std::exception &e)
2043 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2044 <<", exception: "<<e.what()<<std::endl;
2048 Close database if it was opened
2051 sqlite3_finalize(m_database_read);
2052 if(m_database_write)
2053 sqlite3_finalize(m_database_write);
2055 sqlite3_close(m_database);
2061 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2062 for(; i.atEnd() == false; i++)
2064 MapChunk *chunk = i.getNode()->getValue();
2070 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2072 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2073 if(enable_mapgen_debug_info)
2074 infostream<<"initBlockMake(): "
2075 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2076 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2080 v3s16 blockpos_div = getContainerPos(blockpos, chunksize);
2081 v3s16 blockpos_min = blockpos_div * chunksize;
2082 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2084 // Do nothing if not inside limits (+-1 because of neighbors)
2085 if(blockpos_over_limit(blockpos_min - v3s16(1,1,1)) ||
2086 blockpos_over_limit(blockpos_max + v3s16(1,1,1)))
2092 data->no_op = false;
2093 data->seed = m_seed;
2094 data->blockpos_min = blockpos_min;
2095 data->blockpos_max = blockpos_max;
2096 data->blockpos_requested = blockpos;
2097 data->nodedef = m_gamedef->ndef();
2100 Create the whole area of this and the neighboring blocks
2103 //TimeTaker timer("initBlockMake() create area");
2105 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2106 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2108 v2s16 sectorpos(x, z);
2109 // Sector metadata is loaded from disk if not already loaded.
2110 ServerMapSector *sector = createSector(sectorpos);
2113 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2116 //MapBlock *block = createBlock(p);
2117 // 1) get from memory, 2) load from disk
2118 MapBlock *block = emergeBlock(p, false);
2119 // 3) create a blank one
2122 block = createBlock(p);
2125 Block gets sunlight if this is true.
2127 Refer to the map generator heuristics.
2129 bool ug = mapgen::block_is_underground(data->seed, p);
2130 block->setIsUnderground(ug);
2133 // Lighting will not be valid after make_chunk is called
2134 block->setLightingExpired(true);
2135 // Lighting will be calculated
2136 //block->setLightingExpired(false);
2142 Now we have a big empty area.
2144 Make a ManualMapVoxelManipulator that contains this and the
2148 // The area that contains this block and it's neighbors
2149 v3s16 bigarea_blocks_min = blockpos_min - v3s16(1,1,1);
2150 v3s16 bigarea_blocks_max = blockpos_max + v3s16(1,1,1);
2152 data->vmanip = new ManualMapVoxelManipulator(this);
2153 //data->vmanip->setMap(this);
2157 //TimeTaker timer("initBlockMake() initialEmerge");
2158 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2161 // Data is ready now.
2164 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2165 core::map<v3s16, MapBlock*> &changed_blocks)
2167 v3s16 blockpos_min = data->blockpos_min;
2168 v3s16 blockpos_max = data->blockpos_max;
2169 v3s16 blockpos_requested = data->blockpos_requested;
2170 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2171 <<blockpos_requested.Y<<","
2172 <<blockpos_requested.Z<<")"<<std::endl;*/
2176 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2180 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2182 /*infostream<<"Resulting vmanip:"<<std::endl;
2183 data->vmanip.print(infostream);*/
2185 // Make sure affected blocks are loaded
2186 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2187 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2188 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2191 // Load from disk if not already in memory
2192 emergeBlock(p, false);
2196 Blit generated stuff to map
2197 NOTE: blitBackAll adds nearly everything to changed_blocks
2201 //TimeTaker timer("finishBlockMake() blitBackAll");
2202 data->vmanip->blitBackAll(&changed_blocks);
2205 if(enable_mapgen_debug_info)
2206 infostream<<"finishBlockMake: changed_blocks.size()="
2207 <<changed_blocks.size()<<std::endl;
2210 Copy transforming liquid information
2212 while(data->transforming_liquid.size() > 0)
2214 v3s16 p = data->transforming_liquid.pop_front();
2215 m_transforming_liquid.push_back(p);
2219 Do stuff in central blocks
2226 TimeTaker t("finishBlockMake lighting update");
2228 core::map<v3s16, MapBlock*> lighting_update_blocks;
2231 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2232 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2233 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2236 MapBlock *block = getBlockNoCreateNoEx(p);
2238 lighting_update_blocks.insert(block->getPos(), block);
2241 updateLighting(lighting_update_blocks, changed_blocks);
2244 Set lighting to non-expired state in all of them.
2245 This is cheating, but it is not fast enough if all of them
2246 would actually be updated.
2248 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2249 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2250 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2253 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2256 if(enable_mapgen_debug_info == false)
2257 t.stop(true); // Hide output
2261 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2262 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2263 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2266 MapBlock *block = getBlockNoCreateNoEx(p);
2270 Add random objects to block
2272 mapgen::add_random_objects(block);
2276 Go through changed blocks
2278 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2279 i.atEnd() == false; i++)
2281 MapBlock *block = i.getNode()->getValue();
2284 Update day/night difference cache of the MapBlocks
2286 block->updateDayNightDiff();
2288 Set block as modified
2290 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2291 "finishBlockMake updateDayNightDiff");
2295 Set central blocks as generated
2297 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2298 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2299 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2302 MapBlock *block = getBlockNoCreateNoEx(p);
2304 block->setGenerated(true);
2308 Save changed parts of map
2309 NOTE: Will be saved later.
2311 //save(MOD_STATE_WRITE_AT_UNLOAD);
2313 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2314 <<","<<blockpos_requested.Y<<","
2315 <<blockpos_requested.Z<<")"<<std::endl;*/
2317 if(enable_mapgen_debug_info)
2320 Analyze resulting blocks
2322 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2323 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2324 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2325 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2326 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2327 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2329 v3s16 p = v3s16(x,y,z);
2330 MapBlock *block = getBlockNoCreateNoEx(p);
2332 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2333 infostream<<"Generated "<<spos<<": "
2334 <<analyze_block(block)<<std::endl;
2339 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2345 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2347 DSTACKF("%s: p2d=(%d,%d)",
2352 Check if it exists already in memory
2354 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2359 Try to load it from disk (with blocks)
2361 //if(loadSectorFull(p2d) == true)
2364 Try to load metadata from disk
2367 if(loadSectorMeta(p2d) == true)
2369 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2372 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2373 throw InvalidPositionException("");
2379 Do not create over-limit
2381 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2382 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2383 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2384 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2385 throw InvalidPositionException("createSector(): pos. over limit");
2388 Generate blank sector
2391 sector = new ServerMapSector(this, p2d, m_gamedef);
2393 // Sector position on map in nodes
2394 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2399 m_sectors.insert(p2d, sector);
2405 This is a quick-hand function for calling makeBlock().
2407 MapBlock * ServerMap::generateBlock(
2409 core::map<v3s16, MapBlock*> &modified_blocks
2412 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2414 /*infostream<<"generateBlock(): "
2415 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2418 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2420 TimeTaker timer("generateBlock");
2422 //MapBlock *block = original_dummy;
2424 v2s16 p2d(p.X, p.Z);
2425 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2428 Do not generate over-limit
2430 if(blockpos_over_limit(p))
2432 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2433 throw InvalidPositionException("generateBlock(): pos. over limit");
2437 Create block make data
2439 mapgen::BlockMakeData data;
2440 initBlockMake(&data, p);
2446 TimeTaker t("mapgen::make_block()");
2447 mapgen::make_block(&data);
2449 if(enable_mapgen_debug_info == false)
2450 t.stop(true); // Hide output
2454 Blit data back on map, update lighting, add mobs and whatever this does
2456 finishBlockMake(&data, modified_blocks);
2461 MapBlock *block = getBlockNoCreateNoEx(p);
2469 bool erroneus_content = false;
2470 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2471 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2472 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2475 MapNode n = block->getNode(p);
2476 if(n.getContent() == CONTENT_IGNORE)
2478 infostream<<"CONTENT_IGNORE at "
2479 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2481 erroneus_content = true;
2485 if(erroneus_content)
2494 Generate a completely empty block
2498 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2499 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2501 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2504 n.setContent(CONTENT_AIR);
2505 block->setNode(v3s16(x0,y0,z0), n);
2511 if(enable_mapgen_debug_info == false)
2512 timer.stop(true); // Hide output
2517 MapBlock * ServerMap::createBlock(v3s16 p)
2519 DSTACKF("%s: p=(%d,%d,%d)",
2520 __FUNCTION_NAME, p.X, p.Y, p.Z);
2523 Do not create over-limit
2525 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2526 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2527 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2531 throw InvalidPositionException("createBlock(): pos. over limit");
2533 v2s16 p2d(p.X, p.Z);
2536 This will create or load a sector if not found in memory.
2537 If block exists on disk, it will be loaded.
2539 NOTE: On old save formats, this will be slow, as it generates
2540 lighting on blocks for them.
2542 ServerMapSector *sector;
2544 sector = (ServerMapSector*)createSector(p2d);
2545 assert(sector->getId() == MAPSECTOR_SERVER);
2547 catch(InvalidPositionException &e)
2549 infostream<<"createBlock: createSector() failed"<<std::endl;
2553 NOTE: This should not be done, or at least the exception
2554 should not be passed on as std::exception, because it
2555 won't be catched at all.
2557 /*catch(std::exception &e)
2559 infostream<<"createBlock: createSector() failed: "
2560 <<e.what()<<std::endl;
2565 Try to get a block from the sector
2568 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2571 if(block->isDummy())
2576 block = sector->createBlankBlock(block_y);
2580 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2582 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2584 p.X, p.Y, p.Z, allow_generate);
2587 MapBlock *block = getBlockNoCreateNoEx(p);
2588 if(block && block->isDummy() == false)
2593 MapBlock *block = loadBlock(p);
2600 core::map<v3s16, MapBlock*> modified_blocks;
2601 MapBlock *block = generateBlock(p, modified_blocks);
2605 event.type = MEET_OTHER;
2608 // Copy modified_blocks to event
2609 for(core::map<v3s16, MapBlock*>::Iterator
2610 i = modified_blocks.getIterator();
2611 i.atEnd()==false; i++)
2613 event.modified_blocks.insert(i.getNode()->getKey(), false);
2617 dispatchEvent(&event);
2626 s16 ServerMap::findGroundLevel(v2s16 p2d)
2630 Uh, just do something random...
2632 // Find existing map from top to down
2635 v3s16 p(p2d.X, max, p2d.Y);
2636 for(; p.Y>min; p.Y--)
2638 MapNode n = getNodeNoEx(p);
2639 if(n.getContent() != CONTENT_IGNORE)
2644 // If this node is not air, go to plan b
2645 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2647 // Search existing walkable and return it
2648 for(; p.Y>min; p.Y--)
2650 MapNode n = getNodeNoEx(p);
2651 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2660 Determine from map generator noise functions
2663 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2666 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2667 //return (s16)level;
2670 void ServerMap::createDatabase() {
2673 e = sqlite3_exec(m_database,
2674 "CREATE TABLE IF NOT EXISTS `blocks` ("
2675 "`pos` INT NOT NULL PRIMARY KEY,"
2678 , NULL, NULL, NULL);
2679 if(e == SQLITE_ABORT)
2680 throw FileNotGoodException("Could not create database structure");
2682 infostream<<"ServerMap: Database structure was created";
2685 void ServerMap::verifyDatabase() {
2690 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2691 bool needs_create = false;
2695 Open the database connection
2698 createDirs(m_savedir);
2700 if(!fs::PathExists(dbp))
2701 needs_create = true;
2703 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2704 if(d != SQLITE_OK) {
2705 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2706 throw FileNotGoodException("Cannot open database file");
2712 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2713 if(d != SQLITE_OK) {
2714 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2715 throw FileNotGoodException("Cannot prepare read statement");
2718 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2719 if(d != SQLITE_OK) {
2720 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2721 throw FileNotGoodException("Cannot prepare write statement");
2724 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2725 if(d != SQLITE_OK) {
2726 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2727 throw FileNotGoodException("Cannot prepare read statement");
2730 infostream<<"ServerMap: Database opened"<<std::endl;
2734 bool ServerMap::loadFromFolders() {
2735 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2740 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2741 return (sqlite3_int64)pos.Z*16777216 +
2742 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2745 void ServerMap::createDirs(std::string path)
2747 if(fs::CreateAllDirs(path) == false)
2749 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2750 <<"\""<<path<<"\""<<std::endl;
2751 throw BaseException("ServerMap failed to create directory");
2755 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2761 snprintf(cc, 9, "%.4x%.4x",
2762 (unsigned int)pos.X&0xffff,
2763 (unsigned int)pos.Y&0xffff);
2765 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2767 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2768 (unsigned int)pos.X&0xfff,
2769 (unsigned int)pos.Y&0xfff);
2771 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2777 v2s16 ServerMap::getSectorPos(std::string dirname)
2781 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2782 assert(spos != std::string::npos);
2783 if(dirname.size() - spos == 8)
2786 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2788 else if(dirname.size() - spos == 3)
2791 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2792 // Sign-extend the 12 bit values up to 16 bits...
2793 if(x&0x800) x|=0xF000;
2794 if(y&0x800) y|=0xF000;
2801 v2s16 pos((s16)x, (s16)y);
2805 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2807 v2s16 p2d = getSectorPos(sectordir);
2809 if(blockfile.size() != 4){
2810 throw InvalidFilenameException("Invalid block filename");
2813 int r = sscanf(blockfile.c_str(), "%4x", &y);
2815 throw InvalidFilenameException("Invalid block filename");
2816 return v3s16(p2d.X, y, p2d.Y);
2819 std::string ServerMap::getBlockFilename(v3s16 p)
2822 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2826 void ServerMap::save(ModifiedState save_level)
2828 DSTACK(__FUNCTION_NAME);
2829 if(m_map_saving_enabled == false)
2831 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2835 if(save_level == MOD_STATE_CLEAN)
2836 infostream<<"ServerMap: Saving whole map, this can take time."
2839 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2844 // Profile modified reasons
2845 Profiler modprofiler;
2847 u32 sector_meta_count = 0;
2848 u32 block_count = 0;
2849 u32 block_count_all = 0; // Number of blocks in memory
2851 // Don't do anything with sqlite unless something is really saved
2852 bool save_started = false;
2854 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2855 for(; i.atEnd() == false; i++)
2857 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2858 assert(sector->getId() == MAPSECTOR_SERVER);
2860 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2862 saveSectorMeta(sector);
2863 sector_meta_count++;
2865 core::list<MapBlock*> blocks;
2866 sector->getBlocks(blocks);
2867 core::list<MapBlock*>::Iterator j;
2869 for(j=blocks.begin(); j!=blocks.end(); j++)
2871 MapBlock *block = *j;
2875 if(block->getModified() >= save_level)
2880 save_started = true;
2883 modprofiler.add(block->getModifiedReason(), 1);
2888 /*infostream<<"ServerMap: Written block ("
2889 <<block->getPos().X<<","
2890 <<block->getPos().Y<<","
2891 <<block->getPos().Z<<")"
2900 Only print if something happened or saved whole map
2902 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2903 || block_count != 0)
2905 infostream<<"ServerMap: Written: "
2906 <<sector_meta_count<<" sector metadata files, "
2907 <<block_count<<" block files"
2908 <<", "<<block_count_all<<" blocks in memory."
2910 PrintInfo(infostream); // ServerMap/ClientMap:
2911 infostream<<"Blocks modified by: "<<std::endl;
2912 modprofiler.print(infostream);
2916 static s32 unsignedToSigned(s32 i, s32 max_positive)
2918 if(i < max_positive)
2921 return i - 2*max_positive;
2924 // modulo of a negative number does not work consistently in C
2925 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2929 return mod - ((-i) % mod);
2932 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2934 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2936 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2938 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2939 return v3s16(x,y,z);
2942 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2944 if(loadFromFolders()){
2945 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2946 <<"all blocks that are stored in flat files"<<std::endl;
2952 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2954 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2955 v3s16 p = getIntegerAsBlock(block_i);
2956 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2962 void ServerMap::saveMapMeta()
2964 DSTACK(__FUNCTION_NAME);
2966 /*infostream<<"ServerMap::saveMapMeta(): "
2970 createDirs(m_savedir);
2972 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2973 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2974 if(os.good() == false)
2976 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2977 <<"could not open"<<fullpath<<std::endl;
2978 throw FileNotGoodException("Cannot open chunk metadata");
2982 params.setU64("seed", m_seed);
2984 params.writeLines(os);
2986 os<<"[end_of_params]\n";
2988 m_map_metadata_changed = false;
2991 void ServerMap::loadMapMeta()
2993 DSTACK(__FUNCTION_NAME);
2995 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2998 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2999 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3000 if(is.good() == false)
3002 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3003 <<"could not open"<<fullpath<<std::endl;
3004 throw FileNotGoodException("Cannot open map metadata");
3012 throw SerializationError
3013 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3015 std::getline(is, line);
3016 std::string trimmedline = trim(line);
3017 if(trimmedline == "[end_of_params]")
3019 params.parseConfigLine(line);
3022 m_seed = params.getU64("seed");
3024 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3027 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3029 DSTACK(__FUNCTION_NAME);
3030 // Format used for writing
3031 u8 version = SER_FMT_VER_HIGHEST;
3033 v2s16 pos = sector->getPos();
3034 std::string dir = getSectorDir(pos);
3037 std::string fullpath = dir + DIR_DELIM + "meta";
3038 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3039 if(o.good() == false)
3040 throw FileNotGoodException("Cannot open sector metafile");
3042 sector->serialize(o, version);
3044 sector->differs_from_disk = false;
3047 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3049 DSTACK(__FUNCTION_NAME);
3051 v2s16 p2d = getSectorPos(sectordir);
3053 ServerMapSector *sector = NULL;
3055 std::string fullpath = sectordir + DIR_DELIM + "meta";
3056 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3057 if(is.good() == false)
3059 // If the directory exists anyway, it probably is in some old
3060 // format. Just go ahead and create the sector.
3061 if(fs::PathExists(sectordir))
3063 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3064 <<fullpath<<" doesn't exist but directory does."
3065 <<" Continuing with a sector with no metadata."
3067 sector = new ServerMapSector(this, p2d, m_gamedef);
3068 m_sectors.insert(p2d, sector);
3072 throw FileNotGoodException("Cannot open sector metafile");
3077 sector = ServerMapSector::deSerialize
3078 (is, this, p2d, m_sectors, m_gamedef);
3080 saveSectorMeta(sector);
3083 sector->differs_from_disk = false;
3088 bool ServerMap::loadSectorMeta(v2s16 p2d)
3090 DSTACK(__FUNCTION_NAME);
3092 MapSector *sector = NULL;
3094 // The directory layout we're going to load from.
3095 // 1 - original sectors/xxxxzzzz/
3096 // 2 - new sectors2/xxx/zzz/
3097 // If we load from anything but the latest structure, we will
3098 // immediately save to the new one, and remove the old.
3100 std::string sectordir1 = getSectorDir(p2d, 1);
3101 std::string sectordir;
3102 if(fs::PathExists(sectordir1))
3104 sectordir = sectordir1;
3109 sectordir = getSectorDir(p2d, 2);
3113 sector = loadSectorMeta(sectordir, loadlayout != 2);
3115 catch(InvalidFilenameException &e)
3119 catch(FileNotGoodException &e)
3123 catch(std::exception &e)
3132 bool ServerMap::loadSectorFull(v2s16 p2d)
3134 DSTACK(__FUNCTION_NAME);
3136 MapSector *sector = NULL;
3138 // The directory layout we're going to load from.
3139 // 1 - original sectors/xxxxzzzz/
3140 // 2 - new sectors2/xxx/zzz/
3141 // If we load from anything but the latest structure, we will
3142 // immediately save to the new one, and remove the old.
3144 std::string sectordir1 = getSectorDir(p2d, 1);
3145 std::string sectordir;
3146 if(fs::PathExists(sectordir1))
3148 sectordir = sectordir1;
3153 sectordir = getSectorDir(p2d, 2);
3157 sector = loadSectorMeta(sectordir, loadlayout != 2);
3159 catch(InvalidFilenameException &e)
3163 catch(FileNotGoodException &e)
3167 catch(std::exception &e)
3175 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3177 std::vector<fs::DirListNode>::iterator i2;
3178 for(i2=list2.begin(); i2!=list2.end(); i2++)
3184 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3186 catch(InvalidFilenameException &e)
3188 // This catches unknown crap in directory
3194 infostream<<"Sector converted to new layout - deleting "<<
3195 sectordir1<<std::endl;
3196 fs::RecursiveDelete(sectordir1);
3203 void ServerMap::beginSave() {
3205 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3206 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3209 void ServerMap::endSave() {
3211 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3212 infostream<<"WARNING: endSave() failed, map might not have saved.";
3215 void ServerMap::saveBlock(MapBlock *block)
3217 DSTACK(__FUNCTION_NAME);
3219 Dummy blocks are not written
3221 if(block->isDummy())
3223 /*v3s16 p = block->getPos();
3224 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3225 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3229 // Format used for writing
3230 u8 version = SER_FMT_VER_HIGHEST;
3232 v3s16 p3d = block->getPos();
3236 v2s16 p2d(p3d.X, p3d.Z);
3237 std::string sectordir = getSectorDir(p2d);
3239 createDirs(sectordir);
3241 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3242 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3243 if(o.good() == false)
3244 throw FileNotGoodException("Cannot open block data");
3247 [0] u8 serialization version
3253 std::ostringstream o(std::ios_base::binary);
3255 o.write((char*)&version, 1);
3258 block->serialize(o, version, true);
3260 // Write block to database
3262 std::string tmp = o.str();
3263 const char *bytes = tmp.c_str();
3265 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3266 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3267 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3268 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3269 int written = sqlite3_step(m_database_write);
3270 if(written != SQLITE_DONE)
3271 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3272 <<sqlite3_errmsg(m_database)<<std::endl;
3273 // Make ready for later reuse
3274 sqlite3_reset(m_database_write);
3276 // We just wrote it to the disk so clear modified flag
3277 block->resetModified();
3280 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3282 DSTACK(__FUNCTION_NAME);
3284 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3287 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3288 if(is.good() == false)
3289 throw FileNotGoodException("Cannot open block file");
3291 v3s16 p3d = getBlockPos(sectordir, blockfile);
3292 v2s16 p2d(p3d.X, p3d.Z);
3294 assert(sector->getPos() == p2d);
3296 u8 version = SER_FMT_VER_INVALID;
3297 is.read((char*)&version, 1);
3300 throw SerializationError("ServerMap::loadBlock(): Failed"
3301 " to read MapBlock version");
3303 /*u32 block_size = MapBlock::serializedLength(version);
3304 SharedBuffer<u8> data(block_size);
3305 is.read((char*)*data, block_size);*/
3307 // This will always return a sector because we're the server
3308 //MapSector *sector = emergeSector(p2d);
3310 MapBlock *block = NULL;
3311 bool created_new = false;
3312 block = sector->getBlockNoCreateNoEx(p3d.Y);
3315 block = sector->createBlankBlockNoInsert(p3d.Y);
3320 block->deSerialize(is, version, true);
3322 // If it's a new block, insert it to the map
3324 sector->insertBlock(block);
3327 Save blocks loaded in old format in new format
3330 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3334 // Should be in database now, so delete the old file
3335 fs::RecursiveDelete(fullpath);
3338 // We just loaded it from the disk, so it's up-to-date.
3339 block->resetModified();
3342 catch(SerializationError &e)
3344 infostream<<"WARNING: Invalid block data on disk "
3345 <<"fullpath="<<fullpath
3346 <<" (SerializationError). "
3347 <<"what()="<<e.what()
3349 //" Ignoring. A new one will be generated.
3352 // TODO: Backup file; name is in fullpath.
3356 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3358 DSTACK(__FUNCTION_NAME);
3361 std::istringstream is(*blob, std::ios_base::binary);
3363 u8 version = SER_FMT_VER_INVALID;
3364 is.read((char*)&version, 1);
3367 throw SerializationError("ServerMap::loadBlock(): Failed"
3368 " to read MapBlock version");
3370 /*u32 block_size = MapBlock::serializedLength(version);
3371 SharedBuffer<u8> data(block_size);
3372 is.read((char*)*data, block_size);*/
3374 // This will always return a sector because we're the server
3375 //MapSector *sector = emergeSector(p2d);
3377 MapBlock *block = NULL;
3378 bool created_new = false;
3379 block = sector->getBlockNoCreateNoEx(p3d.Y);
3382 block = sector->createBlankBlockNoInsert(p3d.Y);
3387 block->deSerialize(is, version, true);
3389 // If it's a new block, insert it to the map
3391 sector->insertBlock(block);
3394 Save blocks loaded in old format in new format
3397 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3398 // Only save if asked to; no need to update version
3402 // We just loaded it from, so it's up-to-date.
3403 block->resetModified();
3406 catch(SerializationError &e)
3408 infostream<<"WARNING: Invalid block data in database "
3409 <<" (SerializationError). "
3410 <<"what()="<<e.what()
3412 //" Ignoring. A new one will be generated.
3415 // TODO: Copy to a backup database.
3419 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3421 DSTACK(__FUNCTION_NAME);
3423 v2s16 p2d(blockpos.X, blockpos.Z);
3425 if(!loadFromFolders()) {
3428 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3429 infostream<<"WARNING: Could not bind block position for load: "
3430 <<sqlite3_errmsg(m_database)<<std::endl;
3431 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3433 Make sure sector is loaded
3435 MapSector *sector = createSector(p2d);
3440 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3441 size_t len = sqlite3_column_bytes(m_database_read, 0);
3443 std::string datastr(data, len);
3445 loadBlock(&datastr, blockpos, sector, false);
3447 sqlite3_step(m_database_read);
3448 // We should never get more than 1 row, so ok to reset
3449 sqlite3_reset(m_database_read);
3451 return getBlockNoCreateNoEx(blockpos);
3453 sqlite3_reset(m_database_read);
3455 // Not found in database, try the files
3458 // The directory layout we're going to load from.
3459 // 1 - original sectors/xxxxzzzz/
3460 // 2 - new sectors2/xxx/zzz/
3461 // If we load from anything but the latest structure, we will
3462 // immediately save to the new one, and remove the old.
3464 std::string sectordir1 = getSectorDir(p2d, 1);
3465 std::string sectordir;
3466 if(fs::PathExists(sectordir1))
3468 sectordir = sectordir1;
3473 sectordir = getSectorDir(p2d, 2);
3477 Make sure sector is loaded
3479 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3483 sector = loadSectorMeta(sectordir, loadlayout != 2);
3485 catch(InvalidFilenameException &e)
3489 catch(FileNotGoodException &e)
3493 catch(std::exception &e)
3500 Make sure file exists
3503 std::string blockfilename = getBlockFilename(blockpos);
3504 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3508 Load block and save it to the database
3510 loadBlock(sectordir, blockfilename, sector, true);
3511 return getBlockNoCreateNoEx(blockpos);
3514 void ServerMap::PrintInfo(std::ostream &out)
3523 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3528 MapVoxelManipulator::~MapVoxelManipulator()
3530 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3534 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3536 TimeTaker timer1("emerge", &emerge_time);
3538 // Units of these are MapBlocks
3539 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3540 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3542 VoxelArea block_area_nodes
3543 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3545 addArea(block_area_nodes);
3547 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3548 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3549 for(s32 x=p_min.X; x<=p_max.X; x++)
3552 core::map<v3s16, bool>::Node *n;
3553 n = m_loaded_blocks.find(p);
3557 bool block_data_inexistent = false;
3560 TimeTaker timer1("emerge load", &emerge_load_time);
3562 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3563 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3565 a.print(infostream);
3566 infostream<<std::endl;*/
3568 MapBlock *block = m_map->getBlockNoCreate(p);
3569 if(block->isDummy())
3570 block_data_inexistent = true;
3572 block->copyTo(*this);
3574 catch(InvalidPositionException &e)
3576 block_data_inexistent = true;
3579 if(block_data_inexistent)
3581 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3582 // Fill with VOXELFLAG_INEXISTENT
3583 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3584 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3586 s32 i = m_area.index(a.MinEdge.X,y,z);
3587 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3591 m_loaded_blocks.insert(p, !block_data_inexistent);
3594 //infostream<<"emerge done"<<std::endl;
3598 SUGG: Add an option to only update eg. water and air nodes.
3599 This will make it interfere less with important stuff if
3602 void MapVoxelManipulator::blitBack
3603 (core::map<v3s16, MapBlock*> & modified_blocks)
3605 if(m_area.getExtent() == v3s16(0,0,0))
3608 //TimeTaker timer1("blitBack");
3610 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3611 <<m_loaded_blocks.size()<<std::endl;*/
3614 Initialize block cache
3616 v3s16 blockpos_last;
3617 MapBlock *block = NULL;
3618 bool block_checked_in_modified = false;
3620 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3621 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3622 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3626 u8 f = m_flags[m_area.index(p)];
3627 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3630 MapNode &n = m_data[m_area.index(p)];
3632 v3s16 blockpos = getNodeBlockPos(p);
3637 if(block == NULL || blockpos != blockpos_last){
3638 block = m_map->getBlockNoCreate(blockpos);
3639 blockpos_last = blockpos;
3640 block_checked_in_modified = false;
3643 // Calculate relative position in block
3644 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3646 // Don't continue if nothing has changed here
3647 if(block->getNode(relpos) == n)
3650 //m_map->setNode(m_area.MinEdge + p, n);
3651 block->setNode(relpos, n);
3654 Make sure block is in modified_blocks
3656 if(block_checked_in_modified == false)
3658 modified_blocks[blockpos] = block;
3659 block_checked_in_modified = true;
3662 catch(InvalidPositionException &e)
3668 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3669 MapVoxelManipulator(map),
3670 m_create_area(false)
3674 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3678 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3680 // Just create the area so that it can be pointed to
3681 VoxelManipulator::emerge(a, caller_id);
3684 void ManualMapVoxelManipulator::initialEmerge(
3685 v3s16 blockpos_min, v3s16 blockpos_max)
3687 TimeTaker timer1("initialEmerge", &emerge_time);
3689 // Units of these are MapBlocks
3690 v3s16 p_min = blockpos_min;
3691 v3s16 p_max = blockpos_max;
3693 VoxelArea block_area_nodes
3694 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3696 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3699 infostream<<"initialEmerge: area: ";
3700 block_area_nodes.print(infostream);
3701 infostream<<" ("<<size_MB<<"MB)";
3702 infostream<<std::endl;
3705 addArea(block_area_nodes);
3707 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3708 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3709 for(s32 x=p_min.X; x<=p_max.X; x++)
3712 core::map<v3s16, bool>::Node *n;
3713 n = m_loaded_blocks.find(p);
3717 bool block_data_inexistent = false;
3720 TimeTaker timer1("emerge load", &emerge_load_time);
3722 MapBlock *block = m_map->getBlockNoCreate(p);
3723 if(block->isDummy())
3724 block_data_inexistent = true;
3726 block->copyTo(*this);
3728 catch(InvalidPositionException &e)
3730 block_data_inexistent = true;
3733 if(block_data_inexistent)
3736 Mark area inexistent
3738 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3739 // Fill with VOXELFLAG_INEXISTENT
3740 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3741 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3743 s32 i = m_area.index(a.MinEdge.X,y,z);
3744 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3748 m_loaded_blocks.insert(p, !block_data_inexistent);
3752 void ManualMapVoxelManipulator::blitBackAll(
3753 core::map<v3s16, MapBlock*> * modified_blocks)
3755 if(m_area.getExtent() == v3s16(0,0,0))
3759 Copy data of all blocks
3761 for(core::map<v3s16, bool>::Iterator
3762 i = m_loaded_blocks.getIterator();
3763 i.atEnd() == false; i++)
3765 v3s16 p = i.getNode()->getKey();
3766 bool existed = i.getNode()->getValue();
3767 if(existed == false)
3769 // The Great Bug was found using this
3770 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3771 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3775 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3778 infostream<<"WARNING: "<<__FUNCTION_NAME
3779 <<": got NULL block "
3780 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3785 block->copyFrom(*this);
3788 modified_blocks->insert(p, block);