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 chunk_offset(-1,-1,-1);
2081 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2082 v3s16 blockpos_min = blockpos_div * chunksize;
2083 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2084 blockpos_min += chunk_offset;
2085 blockpos_max += chunk_offset;
2087 // Do nothing if not inside limits (+-1 because of neighbors)
2088 if(blockpos_over_limit(blockpos_min - v3s16(1,1,1)) ||
2089 blockpos_over_limit(blockpos_max + v3s16(1,1,1)))
2095 data->no_op = false;
2096 data->seed = m_seed;
2097 data->blockpos_min = blockpos_min;
2098 data->blockpos_max = blockpos_max;
2099 data->blockpos_requested = blockpos;
2100 data->nodedef = m_gamedef->ndef();
2103 Create the whole area of this and the neighboring blocks
2106 //TimeTaker timer("initBlockMake() create area");
2108 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2109 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2111 v2s16 sectorpos(x, z);
2112 // Sector metadata is loaded from disk if not already loaded.
2113 ServerMapSector *sector = createSector(sectorpos);
2116 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2119 //MapBlock *block = createBlock(p);
2120 // 1) get from memory, 2) load from disk
2121 MapBlock *block = emergeBlock(p, false);
2122 // 3) create a blank one
2125 block = createBlock(p);
2128 Block gets sunlight if this is true.
2130 Refer to the map generator heuristics.
2132 bool ug = mapgen::block_is_underground(data->seed, p);
2133 block->setIsUnderground(ug);
2136 // Lighting will not be valid after make_chunk is called
2137 block->setLightingExpired(true);
2138 // Lighting will be calculated
2139 //block->setLightingExpired(false);
2145 Now we have a big empty area.
2147 Make a ManualMapVoxelManipulator that contains this and the
2151 // The area that contains this block and it's neighbors
2152 v3s16 bigarea_blocks_min = blockpos_min - v3s16(1,1,1);
2153 v3s16 bigarea_blocks_max = blockpos_max + v3s16(1,1,1);
2155 data->vmanip = new ManualMapVoxelManipulator(this);
2156 //data->vmanip->setMap(this);
2160 //TimeTaker timer("initBlockMake() initialEmerge");
2161 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2164 // Data is ready now.
2167 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2168 core::map<v3s16, MapBlock*> &changed_blocks)
2170 v3s16 blockpos_min = data->blockpos_min;
2171 v3s16 blockpos_max = data->blockpos_max;
2172 v3s16 blockpos_requested = data->blockpos_requested;
2173 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2174 <<blockpos_requested.Y<<","
2175 <<blockpos_requested.Z<<")"<<std::endl;*/
2179 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2183 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2185 /*infostream<<"Resulting vmanip:"<<std::endl;
2186 data->vmanip.print(infostream);*/
2188 // Make sure affected blocks are loaded
2189 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2190 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2191 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2194 // Load from disk if not already in memory
2195 emergeBlock(p, false);
2199 Blit generated stuff to map
2200 NOTE: blitBackAll adds nearly everything to changed_blocks
2204 //TimeTaker timer("finishBlockMake() blitBackAll");
2205 data->vmanip->blitBackAll(&changed_blocks);
2208 if(enable_mapgen_debug_info)
2209 infostream<<"finishBlockMake: changed_blocks.size()="
2210 <<changed_blocks.size()<<std::endl;
2213 Copy transforming liquid information
2215 while(data->transforming_liquid.size() > 0)
2217 v3s16 p = data->transforming_liquid.pop_front();
2218 m_transforming_liquid.push_back(p);
2222 Do stuff in central blocks
2230 TimeTaker t("finishBlockMake lighting update");
2232 core::map<v3s16, MapBlock*> lighting_update_blocks;
2235 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2236 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2237 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2240 MapBlock *block = getBlockNoCreateNoEx(p);
2242 lighting_update_blocks.insert(block->getPos(), block);
2245 updateLighting(lighting_update_blocks, changed_blocks);
2249 Set lighting to non-expired state in all of them.
2250 This is cheating, but it is not fast enough if all of them
2251 would actually be updated.
2253 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2254 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2255 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2258 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2262 if(enable_mapgen_debug_info == false)
2263 t.stop(true); // Hide output
2268 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2269 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2270 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2273 MapBlock *block = getBlockNoCreateNoEx(p);
2277 Add random objects to block
2279 mapgen::add_random_objects(block);
2283 Go through changed blocks
2285 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2286 i.atEnd() == false; i++)
2288 MapBlock *block = i.getNode()->getValue();
2291 Update day/night difference cache of the MapBlocks
2293 block->updateDayNightDiff();
2295 Set block as modified
2297 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2298 "finishBlockMake updateDayNightDiff");
2302 Set central blocks as generated
2304 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2305 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2306 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2309 MapBlock *block = getBlockNoCreateNoEx(p);
2311 block->setGenerated(true);
2315 Save changed parts of map
2316 NOTE: Will be saved later.
2318 //save(MOD_STATE_WRITE_AT_UNLOAD);
2320 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2321 <<","<<blockpos_requested.Y<<","
2322 <<blockpos_requested.Z<<")"<<std::endl;*/
2324 if(enable_mapgen_debug_info)
2327 Analyze resulting blocks
2329 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2330 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2331 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2332 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2333 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2334 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2336 v3s16 p = v3s16(x,y,z);
2337 MapBlock *block = getBlockNoCreateNoEx(p);
2339 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2340 infostream<<"Generated "<<spos<<": "
2341 <<analyze_block(block)<<std::endl;
2346 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2352 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2354 DSTACKF("%s: p2d=(%d,%d)",
2359 Check if it exists already in memory
2361 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2366 Try to load it from disk (with blocks)
2368 //if(loadSectorFull(p2d) == true)
2371 Try to load metadata from disk
2374 if(loadSectorMeta(p2d) == true)
2376 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2379 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2380 throw InvalidPositionException("");
2386 Do not create over-limit
2388 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2389 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2390 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2391 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2392 throw InvalidPositionException("createSector(): pos. over limit");
2395 Generate blank sector
2398 sector = new ServerMapSector(this, p2d, m_gamedef);
2400 // Sector position on map in nodes
2401 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2406 m_sectors.insert(p2d, sector);
2412 This is a quick-hand function for calling makeBlock().
2414 MapBlock * ServerMap::generateBlock(
2416 core::map<v3s16, MapBlock*> &modified_blocks
2419 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2421 /*infostream<<"generateBlock(): "
2422 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2425 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2427 TimeTaker timer("generateBlock");
2429 //MapBlock *block = original_dummy;
2431 v2s16 p2d(p.X, p.Z);
2432 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2435 Do not generate over-limit
2437 if(blockpos_over_limit(p))
2439 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2440 throw InvalidPositionException("generateBlock(): pos. over limit");
2444 Create block make data
2446 mapgen::BlockMakeData data;
2447 initBlockMake(&data, p);
2453 TimeTaker t("mapgen::make_block()");
2454 mapgen::make_block(&data);
2456 if(enable_mapgen_debug_info == false)
2457 t.stop(true); // Hide output
2461 Blit data back on map, update lighting, add mobs and whatever this does
2463 finishBlockMake(&data, modified_blocks);
2468 MapBlock *block = getBlockNoCreateNoEx(p);
2476 bool erroneus_content = false;
2477 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2478 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2479 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2482 MapNode n = block->getNode(p);
2483 if(n.getContent() == CONTENT_IGNORE)
2485 infostream<<"CONTENT_IGNORE at "
2486 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2488 erroneus_content = true;
2492 if(erroneus_content)
2501 Generate a completely empty block
2505 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2506 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2508 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2511 n.setContent(CONTENT_AIR);
2512 block->setNode(v3s16(x0,y0,z0), n);
2518 if(enable_mapgen_debug_info == false)
2519 timer.stop(true); // Hide output
2524 MapBlock * ServerMap::createBlock(v3s16 p)
2526 DSTACKF("%s: p=(%d,%d,%d)",
2527 __FUNCTION_NAME, p.X, p.Y, p.Z);
2530 Do not create over-limit
2532 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2533 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2534 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2535 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2536 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2537 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2538 throw InvalidPositionException("createBlock(): pos. over limit");
2540 v2s16 p2d(p.X, p.Z);
2543 This will create or load a sector if not found in memory.
2544 If block exists on disk, it will be loaded.
2546 NOTE: On old save formats, this will be slow, as it generates
2547 lighting on blocks for them.
2549 ServerMapSector *sector;
2551 sector = (ServerMapSector*)createSector(p2d);
2552 assert(sector->getId() == MAPSECTOR_SERVER);
2554 catch(InvalidPositionException &e)
2556 infostream<<"createBlock: createSector() failed"<<std::endl;
2560 NOTE: This should not be done, or at least the exception
2561 should not be passed on as std::exception, because it
2562 won't be catched at all.
2564 /*catch(std::exception &e)
2566 infostream<<"createBlock: createSector() failed: "
2567 <<e.what()<<std::endl;
2572 Try to get a block from the sector
2575 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2578 if(block->isDummy())
2583 block = sector->createBlankBlock(block_y);
2587 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2589 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2591 p.X, p.Y, p.Z, allow_generate);
2594 MapBlock *block = getBlockNoCreateNoEx(p);
2595 if(block && block->isDummy() == false)
2600 MapBlock *block = loadBlock(p);
2607 core::map<v3s16, MapBlock*> modified_blocks;
2608 MapBlock *block = generateBlock(p, modified_blocks);
2612 event.type = MEET_OTHER;
2615 // Copy modified_blocks to event
2616 for(core::map<v3s16, MapBlock*>::Iterator
2617 i = modified_blocks.getIterator();
2618 i.atEnd()==false; i++)
2620 event.modified_blocks.insert(i.getNode()->getKey(), false);
2624 dispatchEvent(&event);
2633 s16 ServerMap::findGroundLevel(v2s16 p2d)
2637 Uh, just do something random...
2639 // Find existing map from top to down
2642 v3s16 p(p2d.X, max, p2d.Y);
2643 for(; p.Y>min; p.Y--)
2645 MapNode n = getNodeNoEx(p);
2646 if(n.getContent() != CONTENT_IGNORE)
2651 // If this node is not air, go to plan b
2652 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2654 // Search existing walkable and return it
2655 for(; p.Y>min; p.Y--)
2657 MapNode n = getNodeNoEx(p);
2658 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2667 Determine from map generator noise functions
2670 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2673 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2674 //return (s16)level;
2677 void ServerMap::createDatabase() {
2680 e = sqlite3_exec(m_database,
2681 "CREATE TABLE IF NOT EXISTS `blocks` ("
2682 "`pos` INT NOT NULL PRIMARY KEY,"
2685 , NULL, NULL, NULL);
2686 if(e == SQLITE_ABORT)
2687 throw FileNotGoodException("Could not create database structure");
2689 infostream<<"ServerMap: Database structure was created";
2692 void ServerMap::verifyDatabase() {
2697 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2698 bool needs_create = false;
2702 Open the database connection
2705 createDirs(m_savedir);
2707 if(!fs::PathExists(dbp))
2708 needs_create = true;
2710 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2711 if(d != SQLITE_OK) {
2712 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2713 throw FileNotGoodException("Cannot open database file");
2719 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2720 if(d != SQLITE_OK) {
2721 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2722 throw FileNotGoodException("Cannot prepare read statement");
2725 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2726 if(d != SQLITE_OK) {
2727 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2728 throw FileNotGoodException("Cannot prepare write statement");
2731 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2732 if(d != SQLITE_OK) {
2733 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2734 throw FileNotGoodException("Cannot prepare read statement");
2737 infostream<<"ServerMap: Database opened"<<std::endl;
2741 bool ServerMap::loadFromFolders() {
2742 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2747 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2748 return (sqlite3_int64)pos.Z*16777216 +
2749 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2752 void ServerMap::createDirs(std::string path)
2754 if(fs::CreateAllDirs(path) == false)
2756 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2757 <<"\""<<path<<"\""<<std::endl;
2758 throw BaseException("ServerMap failed to create directory");
2762 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2768 snprintf(cc, 9, "%.4x%.4x",
2769 (unsigned int)pos.X&0xffff,
2770 (unsigned int)pos.Y&0xffff);
2772 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2774 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2775 (unsigned int)pos.X&0xfff,
2776 (unsigned int)pos.Y&0xfff);
2778 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2784 v2s16 ServerMap::getSectorPos(std::string dirname)
2788 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2789 assert(spos != std::string::npos);
2790 if(dirname.size() - spos == 8)
2793 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2795 else if(dirname.size() - spos == 3)
2798 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2799 // Sign-extend the 12 bit values up to 16 bits...
2800 if(x&0x800) x|=0xF000;
2801 if(y&0x800) y|=0xF000;
2808 v2s16 pos((s16)x, (s16)y);
2812 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2814 v2s16 p2d = getSectorPos(sectordir);
2816 if(blockfile.size() != 4){
2817 throw InvalidFilenameException("Invalid block filename");
2820 int r = sscanf(blockfile.c_str(), "%4x", &y);
2822 throw InvalidFilenameException("Invalid block filename");
2823 return v3s16(p2d.X, y, p2d.Y);
2826 std::string ServerMap::getBlockFilename(v3s16 p)
2829 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2833 void ServerMap::save(ModifiedState save_level)
2835 DSTACK(__FUNCTION_NAME);
2836 if(m_map_saving_enabled == false)
2838 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2842 if(save_level == MOD_STATE_CLEAN)
2843 infostream<<"ServerMap: Saving whole map, this can take time."
2846 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2851 // Profile modified reasons
2852 Profiler modprofiler;
2854 u32 sector_meta_count = 0;
2855 u32 block_count = 0;
2856 u32 block_count_all = 0; // Number of blocks in memory
2858 // Don't do anything with sqlite unless something is really saved
2859 bool save_started = false;
2861 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2862 for(; i.atEnd() == false; i++)
2864 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2865 assert(sector->getId() == MAPSECTOR_SERVER);
2867 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2869 saveSectorMeta(sector);
2870 sector_meta_count++;
2872 core::list<MapBlock*> blocks;
2873 sector->getBlocks(blocks);
2874 core::list<MapBlock*>::Iterator j;
2876 for(j=blocks.begin(); j!=blocks.end(); j++)
2878 MapBlock *block = *j;
2882 if(block->getModified() >= save_level)
2887 save_started = true;
2890 modprofiler.add(block->getModifiedReason(), 1);
2895 /*infostream<<"ServerMap: Written block ("
2896 <<block->getPos().X<<","
2897 <<block->getPos().Y<<","
2898 <<block->getPos().Z<<")"
2907 Only print if something happened or saved whole map
2909 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2910 || block_count != 0)
2912 infostream<<"ServerMap: Written: "
2913 <<sector_meta_count<<" sector metadata files, "
2914 <<block_count<<" block files"
2915 <<", "<<block_count_all<<" blocks in memory."
2917 PrintInfo(infostream); // ServerMap/ClientMap:
2918 infostream<<"Blocks modified by: "<<std::endl;
2919 modprofiler.print(infostream);
2923 static s32 unsignedToSigned(s32 i, s32 max_positive)
2925 if(i < max_positive)
2928 return i - 2*max_positive;
2931 // modulo of a negative number does not work consistently in C
2932 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2936 return mod - ((-i) % mod);
2939 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2941 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2943 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2945 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2946 return v3s16(x,y,z);
2949 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2951 if(loadFromFolders()){
2952 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2953 <<"all blocks that are stored in flat files"<<std::endl;
2959 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2961 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2962 v3s16 p = getIntegerAsBlock(block_i);
2963 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2969 void ServerMap::saveMapMeta()
2971 DSTACK(__FUNCTION_NAME);
2973 /*infostream<<"ServerMap::saveMapMeta(): "
2977 createDirs(m_savedir);
2979 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2980 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2981 if(os.good() == false)
2983 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2984 <<"could not open"<<fullpath<<std::endl;
2985 throw FileNotGoodException("Cannot open chunk metadata");
2989 params.setU64("seed", m_seed);
2991 params.writeLines(os);
2993 os<<"[end_of_params]\n";
2995 m_map_metadata_changed = false;
2998 void ServerMap::loadMapMeta()
3000 DSTACK(__FUNCTION_NAME);
3002 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3005 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3006 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3007 if(is.good() == false)
3009 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3010 <<"could not open"<<fullpath<<std::endl;
3011 throw FileNotGoodException("Cannot open map metadata");
3019 throw SerializationError
3020 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3022 std::getline(is, line);
3023 std::string trimmedline = trim(line);
3024 if(trimmedline == "[end_of_params]")
3026 params.parseConfigLine(line);
3029 m_seed = params.getU64("seed");
3031 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3034 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3036 DSTACK(__FUNCTION_NAME);
3037 // Format used for writing
3038 u8 version = SER_FMT_VER_HIGHEST;
3040 v2s16 pos = sector->getPos();
3041 std::string dir = getSectorDir(pos);
3044 std::string fullpath = dir + DIR_DELIM + "meta";
3045 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3046 if(o.good() == false)
3047 throw FileNotGoodException("Cannot open sector metafile");
3049 sector->serialize(o, version);
3051 sector->differs_from_disk = false;
3054 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3056 DSTACK(__FUNCTION_NAME);
3058 v2s16 p2d = getSectorPos(sectordir);
3060 ServerMapSector *sector = NULL;
3062 std::string fullpath = sectordir + DIR_DELIM + "meta";
3063 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3064 if(is.good() == false)
3066 // If the directory exists anyway, it probably is in some old
3067 // format. Just go ahead and create the sector.
3068 if(fs::PathExists(sectordir))
3070 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3071 <<fullpath<<" doesn't exist but directory does."
3072 <<" Continuing with a sector with no metadata."
3074 sector = new ServerMapSector(this, p2d, m_gamedef);
3075 m_sectors.insert(p2d, sector);
3079 throw FileNotGoodException("Cannot open sector metafile");
3084 sector = ServerMapSector::deSerialize
3085 (is, this, p2d, m_sectors, m_gamedef);
3087 saveSectorMeta(sector);
3090 sector->differs_from_disk = false;
3095 bool ServerMap::loadSectorMeta(v2s16 p2d)
3097 DSTACK(__FUNCTION_NAME);
3099 MapSector *sector = NULL;
3101 // The directory layout we're going to load from.
3102 // 1 - original sectors/xxxxzzzz/
3103 // 2 - new sectors2/xxx/zzz/
3104 // If we load from anything but the latest structure, we will
3105 // immediately save to the new one, and remove the old.
3107 std::string sectordir1 = getSectorDir(p2d, 1);
3108 std::string sectordir;
3109 if(fs::PathExists(sectordir1))
3111 sectordir = sectordir1;
3116 sectordir = getSectorDir(p2d, 2);
3120 sector = loadSectorMeta(sectordir, loadlayout != 2);
3122 catch(InvalidFilenameException &e)
3126 catch(FileNotGoodException &e)
3130 catch(std::exception &e)
3139 bool ServerMap::loadSectorFull(v2s16 p2d)
3141 DSTACK(__FUNCTION_NAME);
3143 MapSector *sector = NULL;
3145 // The directory layout we're going to load from.
3146 // 1 - original sectors/xxxxzzzz/
3147 // 2 - new sectors2/xxx/zzz/
3148 // If we load from anything but the latest structure, we will
3149 // immediately save to the new one, and remove the old.
3151 std::string sectordir1 = getSectorDir(p2d, 1);
3152 std::string sectordir;
3153 if(fs::PathExists(sectordir1))
3155 sectordir = sectordir1;
3160 sectordir = getSectorDir(p2d, 2);
3164 sector = loadSectorMeta(sectordir, loadlayout != 2);
3166 catch(InvalidFilenameException &e)
3170 catch(FileNotGoodException &e)
3174 catch(std::exception &e)
3182 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3184 std::vector<fs::DirListNode>::iterator i2;
3185 for(i2=list2.begin(); i2!=list2.end(); i2++)
3191 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3193 catch(InvalidFilenameException &e)
3195 // This catches unknown crap in directory
3201 infostream<<"Sector converted to new layout - deleting "<<
3202 sectordir1<<std::endl;
3203 fs::RecursiveDelete(sectordir1);
3210 void ServerMap::beginSave() {
3212 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3213 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3216 void ServerMap::endSave() {
3218 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3219 infostream<<"WARNING: endSave() failed, map might not have saved.";
3222 void ServerMap::saveBlock(MapBlock *block)
3224 DSTACK(__FUNCTION_NAME);
3226 Dummy blocks are not written
3228 if(block->isDummy())
3230 /*v3s16 p = block->getPos();
3231 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3232 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3236 // Format used for writing
3237 u8 version = SER_FMT_VER_HIGHEST;
3239 v3s16 p3d = block->getPos();
3243 v2s16 p2d(p3d.X, p3d.Z);
3244 std::string sectordir = getSectorDir(p2d);
3246 createDirs(sectordir);
3248 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3249 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3250 if(o.good() == false)
3251 throw FileNotGoodException("Cannot open block data");
3254 [0] u8 serialization version
3260 std::ostringstream o(std::ios_base::binary);
3262 o.write((char*)&version, 1);
3265 block->serialize(o, version, true);
3267 // Write block to database
3269 std::string tmp = o.str();
3270 const char *bytes = tmp.c_str();
3272 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3273 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3274 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3275 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3276 int written = sqlite3_step(m_database_write);
3277 if(written != SQLITE_DONE)
3278 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3279 <<sqlite3_errmsg(m_database)<<std::endl;
3280 // Make ready for later reuse
3281 sqlite3_reset(m_database_write);
3283 // We just wrote it to the disk so clear modified flag
3284 block->resetModified();
3287 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3289 DSTACK(__FUNCTION_NAME);
3291 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3294 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3295 if(is.good() == false)
3296 throw FileNotGoodException("Cannot open block file");
3298 v3s16 p3d = getBlockPos(sectordir, blockfile);
3299 v2s16 p2d(p3d.X, p3d.Z);
3301 assert(sector->getPos() == p2d);
3303 u8 version = SER_FMT_VER_INVALID;
3304 is.read((char*)&version, 1);
3307 throw SerializationError("ServerMap::loadBlock(): Failed"
3308 " to read MapBlock version");
3310 /*u32 block_size = MapBlock::serializedLength(version);
3311 SharedBuffer<u8> data(block_size);
3312 is.read((char*)*data, block_size);*/
3314 // This will always return a sector because we're the server
3315 //MapSector *sector = emergeSector(p2d);
3317 MapBlock *block = NULL;
3318 bool created_new = false;
3319 block = sector->getBlockNoCreateNoEx(p3d.Y);
3322 block = sector->createBlankBlockNoInsert(p3d.Y);
3327 block->deSerialize(is, version, true);
3329 // If it's a new block, insert it to the map
3331 sector->insertBlock(block);
3334 Save blocks loaded in old format in new format
3337 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3341 // Should be in database now, so delete the old file
3342 fs::RecursiveDelete(fullpath);
3345 // We just loaded it from the disk, so it's up-to-date.
3346 block->resetModified();
3349 catch(SerializationError &e)
3351 infostream<<"WARNING: Invalid block data on disk "
3352 <<"fullpath="<<fullpath
3353 <<" (SerializationError). "
3354 <<"what()="<<e.what()
3356 //" Ignoring. A new one will be generated.
3359 // TODO: Backup file; name is in fullpath.
3363 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3365 DSTACK(__FUNCTION_NAME);
3368 std::istringstream is(*blob, std::ios_base::binary);
3370 u8 version = SER_FMT_VER_INVALID;
3371 is.read((char*)&version, 1);
3374 throw SerializationError("ServerMap::loadBlock(): Failed"
3375 " to read MapBlock version");
3377 /*u32 block_size = MapBlock::serializedLength(version);
3378 SharedBuffer<u8> data(block_size);
3379 is.read((char*)*data, block_size);*/
3381 // This will always return a sector because we're the server
3382 //MapSector *sector = emergeSector(p2d);
3384 MapBlock *block = NULL;
3385 bool created_new = false;
3386 block = sector->getBlockNoCreateNoEx(p3d.Y);
3389 block = sector->createBlankBlockNoInsert(p3d.Y);
3394 block->deSerialize(is, version, true);
3396 // If it's a new block, insert it to the map
3398 sector->insertBlock(block);
3401 Save blocks loaded in old format in new format
3404 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3405 // Only save if asked to; no need to update version
3409 // We just loaded it from, so it's up-to-date.
3410 block->resetModified();
3413 catch(SerializationError &e)
3415 infostream<<"WARNING: Invalid block data in database "
3416 <<" (SerializationError). "
3417 <<"what()="<<e.what()
3419 //" Ignoring. A new one will be generated.
3422 // TODO: Copy to a backup database.
3426 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3428 DSTACK(__FUNCTION_NAME);
3430 v2s16 p2d(blockpos.X, blockpos.Z);
3432 if(!loadFromFolders()) {
3435 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3436 infostream<<"WARNING: Could not bind block position for load: "
3437 <<sqlite3_errmsg(m_database)<<std::endl;
3438 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3440 Make sure sector is loaded
3442 MapSector *sector = createSector(p2d);
3447 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3448 size_t len = sqlite3_column_bytes(m_database_read, 0);
3450 std::string datastr(data, len);
3452 loadBlock(&datastr, blockpos, sector, false);
3454 sqlite3_step(m_database_read);
3455 // We should never get more than 1 row, so ok to reset
3456 sqlite3_reset(m_database_read);
3458 return getBlockNoCreateNoEx(blockpos);
3460 sqlite3_reset(m_database_read);
3462 // Not found in database, try the files
3465 // The directory layout we're going to load from.
3466 // 1 - original sectors/xxxxzzzz/
3467 // 2 - new sectors2/xxx/zzz/
3468 // If we load from anything but the latest structure, we will
3469 // immediately save to the new one, and remove the old.
3471 std::string sectordir1 = getSectorDir(p2d, 1);
3472 std::string sectordir;
3473 if(fs::PathExists(sectordir1))
3475 sectordir = sectordir1;
3480 sectordir = getSectorDir(p2d, 2);
3484 Make sure sector is loaded
3486 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3490 sector = loadSectorMeta(sectordir, loadlayout != 2);
3492 catch(InvalidFilenameException &e)
3496 catch(FileNotGoodException &e)
3500 catch(std::exception &e)
3507 Make sure file exists
3510 std::string blockfilename = getBlockFilename(blockpos);
3511 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3515 Load block and save it to the database
3517 loadBlock(sectordir, blockfilename, sector, true);
3518 return getBlockNoCreateNoEx(blockpos);
3521 void ServerMap::PrintInfo(std::ostream &out)
3530 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3535 MapVoxelManipulator::~MapVoxelManipulator()
3537 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3541 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3543 TimeTaker timer1("emerge", &emerge_time);
3545 // Units of these are MapBlocks
3546 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3547 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3549 VoxelArea block_area_nodes
3550 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3552 addArea(block_area_nodes);
3554 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3555 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3556 for(s32 x=p_min.X; x<=p_max.X; x++)
3559 core::map<v3s16, bool>::Node *n;
3560 n = m_loaded_blocks.find(p);
3564 bool block_data_inexistent = false;
3567 TimeTaker timer1("emerge load", &emerge_load_time);
3569 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3570 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3572 a.print(infostream);
3573 infostream<<std::endl;*/
3575 MapBlock *block = m_map->getBlockNoCreate(p);
3576 if(block->isDummy())
3577 block_data_inexistent = true;
3579 block->copyTo(*this);
3581 catch(InvalidPositionException &e)
3583 block_data_inexistent = true;
3586 if(block_data_inexistent)
3588 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3589 // Fill with VOXELFLAG_INEXISTENT
3590 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3591 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3593 s32 i = m_area.index(a.MinEdge.X,y,z);
3594 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3598 m_loaded_blocks.insert(p, !block_data_inexistent);
3601 //infostream<<"emerge done"<<std::endl;
3605 SUGG: Add an option to only update eg. water and air nodes.
3606 This will make it interfere less with important stuff if
3609 void MapVoxelManipulator::blitBack
3610 (core::map<v3s16, MapBlock*> & modified_blocks)
3612 if(m_area.getExtent() == v3s16(0,0,0))
3615 //TimeTaker timer1("blitBack");
3617 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3618 <<m_loaded_blocks.size()<<std::endl;*/
3621 Initialize block cache
3623 v3s16 blockpos_last;
3624 MapBlock *block = NULL;
3625 bool block_checked_in_modified = false;
3627 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3628 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3629 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3633 u8 f = m_flags[m_area.index(p)];
3634 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3637 MapNode &n = m_data[m_area.index(p)];
3639 v3s16 blockpos = getNodeBlockPos(p);
3644 if(block == NULL || blockpos != blockpos_last){
3645 block = m_map->getBlockNoCreate(blockpos);
3646 blockpos_last = blockpos;
3647 block_checked_in_modified = false;
3650 // Calculate relative position in block
3651 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3653 // Don't continue if nothing has changed here
3654 if(block->getNode(relpos) == n)
3657 //m_map->setNode(m_area.MinEdge + p, n);
3658 block->setNode(relpos, n);
3661 Make sure block is in modified_blocks
3663 if(block_checked_in_modified == false)
3665 modified_blocks[blockpos] = block;
3666 block_checked_in_modified = true;
3669 catch(InvalidPositionException &e)
3675 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3676 MapVoxelManipulator(map),
3677 m_create_area(false)
3681 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3685 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3687 // Just create the area so that it can be pointed to
3688 VoxelManipulator::emerge(a, caller_id);
3691 void ManualMapVoxelManipulator::initialEmerge(
3692 v3s16 blockpos_min, v3s16 blockpos_max)
3694 TimeTaker timer1("initialEmerge", &emerge_time);
3696 // Units of these are MapBlocks
3697 v3s16 p_min = blockpos_min;
3698 v3s16 p_max = blockpos_max;
3700 VoxelArea block_area_nodes
3701 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3703 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3706 infostream<<"initialEmerge: area: ";
3707 block_area_nodes.print(infostream);
3708 infostream<<" ("<<size_MB<<"MB)";
3709 infostream<<std::endl;
3712 addArea(block_area_nodes);
3714 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3715 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3716 for(s32 x=p_min.X; x<=p_max.X; x++)
3719 core::map<v3s16, bool>::Node *n;
3720 n = m_loaded_blocks.find(p);
3724 bool block_data_inexistent = false;
3727 TimeTaker timer1("emerge load", &emerge_load_time);
3729 MapBlock *block = m_map->getBlockNoCreate(p);
3730 if(block->isDummy())
3731 block_data_inexistent = true;
3733 block->copyTo(*this);
3735 catch(InvalidPositionException &e)
3737 block_data_inexistent = true;
3740 if(block_data_inexistent)
3743 Mark area inexistent
3745 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3746 // Fill with VOXELFLAG_INEXISTENT
3747 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3748 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3750 s32 i = m_area.index(a.MinEdge.X,y,z);
3751 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3755 m_loaded_blocks.insert(p, !block_data_inexistent);
3759 void ManualMapVoxelManipulator::blitBackAll(
3760 core::map<v3s16, MapBlock*> * modified_blocks)
3762 if(m_area.getExtent() == v3s16(0,0,0))
3766 Copy data of all blocks
3768 for(core::map<v3s16, bool>::Iterator
3769 i = m_loaded_blocks.getIterator();
3770 i.atEnd() == false; i++)
3772 v3s16 p = i.getNode()->getKey();
3773 bool existed = i.getNode()->getValue();
3774 if(existed == false)
3776 // The Great Bug was found using this
3777 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3778 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3782 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3785 infostream<<"WARNING: "<<__FUNCTION_NAME
3786 <<": got NULL block "
3787 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3792 block->copyFrom(*this);
3795 modified_blocks->insert(p, block);