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)
802 Enable this to disable proper lighting for speeding up map
803 generation for testing or whatever
806 //if(g_settings->get(""))
808 core::map<v3s16, MapBlock*>::Iterator i;
809 i = blocks_to_update.getIterator();
810 for(; i.atEnd() == false; i++)
812 MapBlock *block = i.getNode()->getValue();
813 v3s16 p = block->getPos();
814 block->setLightingExpired(false);
822 //TimeTaker timer("unspreadLight");
823 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
828 u32 diff = modified_blocks.size() - count_was;
829 count_was = modified_blocks.size();
830 infostream<<"unspreadLight modified "<<diff<<std::endl;
834 //TimeTaker timer("spreadLight");
835 spreadLight(bank, light_sources, modified_blocks);
840 u32 diff = modified_blocks.size() - count_was;
841 count_was = modified_blocks.size();
842 infostream<<"spreadLight modified "<<diff<<std::endl;
848 //MapVoxelManipulator vmanip(this);
850 // Make a manual voxel manipulator and load all the blocks
851 // that touch the requested blocks
852 ManualMapVoxelManipulator vmanip(this);
855 //TimeTaker timer("initialEmerge");
857 core::map<v3s16, MapBlock*>::Iterator i;
858 i = blocks_to_update.getIterator();
859 for(; i.atEnd() == false; i++)
861 MapBlock *block = i.getNode()->getValue();
862 v3s16 p = block->getPos();
864 // Add all surrounding blocks
865 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
868 Add all surrounding blocks that have up-to-date lighting
869 NOTE: This doesn't quite do the job (not everything
870 appropriate is lighted)
872 /*for(s16 z=-1; z<=1; z++)
873 for(s16 y=-1; y<=1; y++)
874 for(s16 x=-1; x<=1; x++)
876 v3s16 p2 = p + v3s16(x,y,z);
877 MapBlock *block = getBlockNoCreateNoEx(p2);
882 if(block->getLightingExpired())
884 vmanip.initialEmerge(p2, p2);
887 // Lighting of block will be updated completely
888 block->setLightingExpired(false);
893 //TimeTaker timer("unSpreadLight");
894 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
897 //TimeTaker timer("spreadLight");
898 vmanip.spreadLight(bank, light_sources, nodemgr);
901 //TimeTaker timer("blitBack");
902 vmanip.blitBack(modified_blocks);
904 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
909 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
912 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
913 core::map<v3s16, MapBlock*> & modified_blocks)
915 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
916 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
919 Update information about whether day and night light differ
921 for(core::map<v3s16, MapBlock*>::Iterator
922 i = modified_blocks.getIterator();
923 i.atEnd() == false; i++)
925 MapBlock *block = i.getNode()->getValue();
926 block->updateDayNightDiff();
932 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
933 core::map<v3s16, MapBlock*> &modified_blocks)
935 INodeDefManager *nodemgr = m_gamedef->ndef();
938 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 From this node to nodes underneath:
943 If lighting is sunlight (1.0), unlight neighbours and
948 v3s16 toppos = p + v3s16(0,1,0);
949 v3s16 bottompos = p + v3s16(0,-1,0);
951 bool node_under_sunlight = true;
952 core::map<v3s16, bool> light_sources;
955 If there is a node at top and it doesn't have sunlight,
956 there has not been any sunlight going down.
958 Otherwise there probably is.
961 MapNode topnode = getNode(toppos);
963 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
964 node_under_sunlight = false;
966 catch(InvalidPositionException &e)
971 Remove all light that has come out of this node
974 enum LightBank banks[] =
979 for(s32 i=0; i<2; i++)
981 enum LightBank bank = banks[i];
983 u8 lightwas = getNode(p).getLight(bank, nodemgr);
985 // Add the block of the added node to modified_blocks
986 v3s16 blockpos = getNodeBlockPos(p);
987 MapBlock * block = getBlockNoCreate(blockpos);
988 assert(block != NULL);
989 modified_blocks.insert(blockpos, block);
991 assert(isValidPosition(p));
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1000 n.setLight(bank, 0, nodemgr);
1004 If node lets sunlight through and is under sunlight, it has
1007 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1009 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1013 Set the node on the map
1022 std::string metadata_name = nodemgr->get(n).metadata_name;
1023 if(metadata_name != ""){
1024 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1026 errorstream<<"Failed to create node metadata \""
1027 <<metadata_name<<"\""<<std::endl;
1029 setNodeMetadata(p, meta);
1034 If node is under sunlight and doesn't let sunlight through,
1035 take all sunlighted nodes under it and clear light from them
1036 and from where the light has been spread.
1037 TODO: This could be optimized by mass-unlighting instead
1040 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1044 //m_dout<<DTIME<<"y="<<y<<std::endl;
1045 v3s16 n2pos(p.X, y, p.Z);
1049 n2 = getNode(n2pos);
1051 catch(InvalidPositionException &e)
1056 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1058 unLightNeighbors(LIGHTBANK_DAY,
1059 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1060 light_sources, modified_blocks);
1061 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Spread light from all nodes that might be capable of doing so
1076 spreadLight(bank, light_sources, modified_blocks);
1080 Update information about whether day and night light differ
1082 for(core::map<v3s16, MapBlock*>::Iterator
1083 i = modified_blocks.getIterator();
1084 i.atEnd() == false; i++)
1086 MapBlock *block = i.getNode()->getValue();
1087 block->updateDayNightDiff();
1091 Add neighboring liquid nodes and the node itself if it is
1092 liquid (=water node was added) to transform queue.
1095 v3s16(0,0,0), // self
1096 v3s16(0,0,1), // back
1097 v3s16(0,1,0), // top
1098 v3s16(1,0,0), // right
1099 v3s16(0,0,-1), // front
1100 v3s16(0,-1,0), // bottom
1101 v3s16(-1,0,0), // left
1103 for(u16 i=0; i<7; i++)
1108 v3s16 p2 = p + dirs[i];
1110 MapNode n2 = getNode(p2);
1111 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1113 m_transforming_liquid.push_back(p2);
1116 }catch(InvalidPositionException &e)
1124 void Map::removeNodeAndUpdate(v3s16 p,
1125 core::map<v3s16, MapBlock*> &modified_blocks)
1127 INodeDefManager *nodemgr = m_gamedef->ndef();
1129 /*PrintInfo(m_dout);
1130 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1131 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1133 bool node_under_sunlight = true;
1135 v3s16 toppos = p + v3s16(0,1,0);
1137 // Node will be replaced with this
1138 content_t replace_material = CONTENT_AIR;
1141 If there is a node at top and it doesn't have sunlight,
1142 there will be no sunlight going down.
1145 MapNode topnode = getNode(toppos);
1147 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1148 node_under_sunlight = false;
1150 catch(InvalidPositionException &e)
1154 core::map<v3s16, bool> light_sources;
1156 enum LightBank banks[] =
1161 for(s32 i=0; i<2; i++)
1163 enum LightBank bank = banks[i];
1166 Unlight neighbors (in case the node is a light source)
1168 unLightNeighbors(bank, p,
1169 getNode(p).getLight(bank, nodemgr),
1170 light_sources, modified_blocks);
1174 Remove node metadata
1177 removeNodeMetadata(p);
1181 This also clears the lighting.
1185 n.setContent(replace_material);
1188 for(s32 i=0; i<2; i++)
1190 enum LightBank bank = banks[i];
1193 Recalculate lighting
1195 spreadLight(bank, light_sources, modified_blocks);
1198 // Add the block of the removed node to modified_blocks
1199 v3s16 blockpos = getNodeBlockPos(p);
1200 MapBlock * block = getBlockNoCreate(blockpos);
1201 assert(block != NULL);
1202 modified_blocks.insert(blockpos, block);
1205 If the removed node was under sunlight, propagate the
1206 sunlight down from it and then light all neighbors
1207 of the propagated blocks.
1209 if(node_under_sunlight)
1211 s16 ybottom = propagateSunlight(p, modified_blocks);
1212 /*m_dout<<DTIME<<"Node was under sunlight. "
1213 "Propagating sunlight";
1214 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1216 for(; y >= ybottom; y--)
1218 v3s16 p2(p.X, y, p.Z);
1219 /*m_dout<<DTIME<<"lighting neighbors of node ("
1220 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1222 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1227 // Set the lighting of this node to 0
1228 // TODO: Is this needed? Lighting is cleared up there already.
1230 MapNode n = getNode(p);
1231 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1234 catch(InvalidPositionException &e)
1240 for(s32 i=0; i<2; i++)
1242 enum LightBank bank = banks[i];
1244 // Get the brightest neighbour node and propagate light from it
1245 v3s16 n2p = getBrightestNeighbour(bank, p);
1247 MapNode n2 = getNode(n2p);
1248 lightNeighbors(bank, n2p, modified_blocks);
1250 catch(InvalidPositionException &e)
1256 Update information about whether day and night light differ
1258 for(core::map<v3s16, MapBlock*>::Iterator
1259 i = modified_blocks.getIterator();
1260 i.atEnd() == false; i++)
1262 MapBlock *block = i.getNode()->getValue();
1263 block->updateDayNightDiff();
1267 Add neighboring liquid nodes and this node to transform queue.
1268 (it's vital for the node itself to get updated last.)
1271 v3s16(0,0,1), // back
1272 v3s16(0,1,0), // top
1273 v3s16(1,0,0), // right
1274 v3s16(0,0,-1), // front
1275 v3s16(0,-1,0), // bottom
1276 v3s16(-1,0,0), // left
1277 v3s16(0,0,0), // self
1279 for(u16 i=0; i<7; i++)
1284 v3s16 p2 = p + dirs[i];
1286 MapNode n2 = getNode(p2);
1287 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1289 m_transforming_liquid.push_back(p2);
1292 }catch(InvalidPositionException &e)
1298 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1301 event.type = MEET_ADDNODE;
1305 bool succeeded = true;
1307 core::map<v3s16, MapBlock*> modified_blocks;
1308 addNodeAndUpdate(p, n, modified_blocks);
1310 // Copy modified_blocks to event
1311 for(core::map<v3s16, MapBlock*>::Iterator
1312 i = modified_blocks.getIterator();
1313 i.atEnd()==false; i++)
1315 event.modified_blocks.insert(i.getNode()->getKey(), false);
1318 catch(InvalidPositionException &e){
1322 dispatchEvent(&event);
1327 bool Map::removeNodeWithEvent(v3s16 p)
1330 event.type = MEET_REMOVENODE;
1333 bool succeeded = true;
1335 core::map<v3s16, MapBlock*> modified_blocks;
1336 removeNodeAndUpdate(p, modified_blocks);
1338 // Copy modified_blocks to event
1339 for(core::map<v3s16, MapBlock*>::Iterator
1340 i = modified_blocks.getIterator();
1341 i.atEnd()==false; i++)
1343 event.modified_blocks.insert(i.getNode()->getKey(), false);
1346 catch(InvalidPositionException &e){
1350 dispatchEvent(&event);
1355 bool Map::dayNightDiffed(v3s16 blockpos)
1358 v3s16 p = blockpos + v3s16(0,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1366 v3s16 p = blockpos + v3s16(-1,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->dayNightDiffed())
1371 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(0,-1,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,0,-1);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(1,0,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->dayNightDiffed())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,1,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->dayNightDiffed())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,0,1);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->dayNightDiffed())
1407 catch(InvalidPositionException &e){}
1413 Updates usage timers
1415 void Map::timerUpdate(float dtime, float unload_timeout,
1416 core::list<v3s16> *unloaded_blocks)
1418 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1420 // Profile modified reasons
1421 Profiler modprofiler;
1423 core::list<v2s16> sector_deletion_queue;
1424 u32 deleted_blocks_count = 0;
1425 u32 saved_blocks_count = 0;
1426 u32 block_count_all = 0;
1428 core::map<v2s16, MapSector*>::Iterator si;
1431 si = m_sectors.getIterator();
1432 for(; si.atEnd() == false; si++)
1434 MapSector *sector = si.getNode()->getValue();
1436 bool all_blocks_deleted = true;
1438 core::list<MapBlock*> blocks;
1439 sector->getBlocks(blocks);
1441 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1442 i != blocks.end(); i++)
1444 MapBlock *block = (*i);
1446 block->incrementUsageTimer(dtime);
1448 if(block->getUsageTimer() > unload_timeout)
1450 v3s16 p = block->getPos();
1453 if(block->getModified() != MOD_STATE_CLEAN
1454 && save_before_unloading)
1456 modprofiler.add(block->getModifiedReason(), 1);
1458 saved_blocks_count++;
1461 // Delete from memory
1462 sector->deleteBlock(block);
1465 unloaded_blocks->push_back(p);
1467 deleted_blocks_count++;
1471 all_blocks_deleted = false;
1476 if(all_blocks_deleted)
1478 sector_deletion_queue.push_back(si.getNode()->getKey());
1483 // Finally delete the empty sectors
1484 deleteSectors(sector_deletion_queue);
1486 if(deleted_blocks_count != 0)
1488 PrintInfo(infostream); // ServerMap/ClientMap:
1489 infostream<<"Unloaded "<<deleted_blocks_count
1490 <<" blocks from memory";
1491 if(save_before_unloading)
1492 infostream<<", of which "<<saved_blocks_count<<" were written";
1493 infostream<<", "<<block_count_all<<" blocks in memory";
1494 infostream<<"."<<std::endl;
1495 if(saved_blocks_count != 0){
1496 PrintInfo(infostream); // ServerMap/ClientMap:
1497 infostream<<"Blocks modified by: "<<std::endl;
1498 modprofiler.print(infostream);
1503 void Map::deleteSectors(core::list<v2s16> &list)
1505 core::list<v2s16>::Iterator j;
1506 for(j=list.begin(); j!=list.end(); j++)
1508 MapSector *sector = m_sectors[*j];
1509 // If sector is in sector cache, remove it from there
1510 if(m_sector_cache == sector)
1511 m_sector_cache = NULL;
1512 // Remove from map and delete
1513 m_sectors.remove(*j);
1519 void Map::unloadUnusedData(float timeout,
1520 core::list<v3s16> *deleted_blocks)
1522 core::list<v2s16> sector_deletion_queue;
1523 u32 deleted_blocks_count = 0;
1524 u32 saved_blocks_count = 0;
1526 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1527 for(; si.atEnd() == false; si++)
1529 MapSector *sector = si.getNode()->getValue();
1531 bool all_blocks_deleted = true;
1533 core::list<MapBlock*> blocks;
1534 sector->getBlocks(blocks);
1535 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1536 i != blocks.end(); i++)
1538 MapBlock *block = (*i);
1540 if(block->getUsageTimer() > timeout)
1543 if(block->getModified() != MOD_STATE_CLEAN)
1546 saved_blocks_count++;
1548 // Delete from memory
1549 sector->deleteBlock(block);
1550 deleted_blocks_count++;
1554 all_blocks_deleted = false;
1558 if(all_blocks_deleted)
1560 sector_deletion_queue.push_back(si.getNode()->getKey());
1564 deleteSectors(sector_deletion_queue);
1566 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1567 <<", of which "<<saved_blocks_count<<" were wr."
1570 //return sector_deletion_queue.getSize();
1571 //return deleted_blocks_count;
1575 void Map::PrintInfo(std::ostream &out)
1580 #define WATER_DROP_BOOST 4
1584 NEIGHBOR_SAME_LEVEL,
1587 struct NodeNeighbor {
1593 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1595 INodeDefManager *nodemgr = m_gamedef->ndef();
1597 DSTACK(__FUNCTION_NAME);
1598 //TimeTaker timer("transformLiquids()");
1601 u32 initial_size = m_transforming_liquid.size();
1603 /*if(initial_size != 0)
1604 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1606 // list of nodes that due to viscosity have not reached their max level height
1607 UniqueQueue<v3s16> must_reflow;
1609 // List of MapBlocks that will require a lighting update (due to lava)
1610 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1612 while(m_transforming_liquid.size() != 0)
1614 // This should be done here so that it is done when continue is used
1615 if(loopcount >= initial_size * 3)
1620 Get a queued transforming liquid node
1622 v3s16 p0 = m_transforming_liquid.pop_front();
1624 MapNode n0 = getNodeNoEx(p0);
1627 Collect information about current node
1629 s8 liquid_level = -1;
1630 u8 liquid_kind = CONTENT_IGNORE;
1631 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1632 switch (liquid_type) {
1634 liquid_level = LIQUID_LEVEL_SOURCE;
1635 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1637 case LIQUID_FLOWING:
1638 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1639 liquid_kind = n0.getContent();
1642 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1643 // continue with the next node.
1644 if (n0.getContent() != CONTENT_AIR)
1646 liquid_kind = CONTENT_AIR;
1651 Collect information about the environment
1653 const v3s16 *dirs = g_6dirs;
1654 NodeNeighbor sources[6]; // surrounding sources
1655 int num_sources = 0;
1656 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1658 NodeNeighbor airs[6]; // surrounding air
1660 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1661 int num_neutrals = 0;
1662 bool flowing_down = false;
1663 for (u16 i = 0; i < 6; i++) {
1664 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1667 nt = NEIGHBOR_UPPER;
1670 nt = NEIGHBOR_LOWER;
1673 v3s16 npos = p0 + dirs[i];
1674 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1675 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1677 if (nb.n.getContent() == CONTENT_AIR) {
1678 airs[num_airs++] = nb;
1679 // if the current node is a water source the neighbor
1680 // should be enqueded for transformation regardless of whether the
1681 // current node changes or not.
1682 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1683 m_transforming_liquid.push_back(npos);
1684 // if the current node happens to be a flowing node, it will start to flow down here.
1685 if (nb.t == NEIGHBOR_LOWER) {
1686 flowing_down = true;
1689 neutrals[num_neutrals++] = nb;
1693 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1694 if (liquid_kind == CONTENT_AIR)
1695 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1696 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1697 neutrals[num_neutrals++] = nb;
1699 // Do not count bottom source, it will screw things up
1701 sources[num_sources++] = nb;
1704 case LIQUID_FLOWING:
1705 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1706 if (liquid_kind == CONTENT_AIR)
1707 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1708 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1709 neutrals[num_neutrals++] = nb;
1711 flows[num_flows++] = nb;
1712 if (nb.t == NEIGHBOR_LOWER)
1713 flowing_down = true;
1720 decide on the type (and possibly level) of the current node
1722 content_t new_node_content;
1723 s8 new_node_level = -1;
1724 s8 max_node_level = -1;
1725 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1726 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1727 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1728 // it's perfectly safe to use liquid_kind here to determine the new node content.
1729 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1730 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1731 // liquid_kind is set properly, see above
1732 new_node_content = liquid_kind;
1733 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1735 // no surrounding sources, so get the maximum level that can flow into this node
1736 for (u16 i = 0; i < num_flows; i++) {
1737 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1738 switch (flows[i].t) {
1739 case NEIGHBOR_UPPER:
1740 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1741 max_node_level = LIQUID_LEVEL_MAX;
1742 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1743 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1744 } else if (nb_liquid_level > max_node_level)
1745 max_node_level = nb_liquid_level;
1747 case NEIGHBOR_LOWER:
1749 case NEIGHBOR_SAME_LEVEL:
1750 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1751 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1752 max_node_level = nb_liquid_level - 1;
1758 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1759 if (viscosity > 1 && max_node_level != liquid_level) {
1760 // amount to gain, limited by viscosity
1761 // must be at least 1 in absolute value
1762 s8 level_inc = max_node_level - liquid_level;
1763 if (level_inc < -viscosity || level_inc > viscosity)
1764 new_node_level = liquid_level + level_inc/viscosity;
1765 else if (level_inc < 0)
1766 new_node_level = liquid_level - 1;
1767 else if (level_inc > 0)
1768 new_node_level = liquid_level + 1;
1769 if (new_node_level != max_node_level)
1770 must_reflow.push_back(p0);
1772 new_node_level = max_node_level;
1774 if (new_node_level >= 0)
1775 new_node_content = liquid_kind;
1777 new_node_content = CONTENT_AIR;
1782 check if anything has changed. if not, just continue with the next node.
1784 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1785 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1786 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1792 update the current node
1794 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1795 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1796 // set level to last 3 bits, flowing down bit to 4th bit
1797 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1799 // set the liquid level and flow bit to 0
1800 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1802 n0.setContent(new_node_content);
1804 v3s16 blockpos = getNodeBlockPos(p0);
1805 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1807 modified_blocks.insert(blockpos, block);
1808 // If node emits light, MapBlock requires lighting update
1809 if(nodemgr->get(n0).light_source != 0)
1810 lighting_modified_blocks[block->getPos()] = block;
1814 enqueue neighbors for update if neccessary
1816 switch (nodemgr->get(n0.getContent()).liquid_type) {
1818 case LIQUID_FLOWING:
1819 // make sure source flows into all neighboring nodes
1820 for (u16 i = 0; i < num_flows; i++)
1821 if (flows[i].t != NEIGHBOR_UPPER)
1822 m_transforming_liquid.push_back(flows[i].p);
1823 for (u16 i = 0; i < num_airs; i++)
1824 if (airs[i].t != NEIGHBOR_UPPER)
1825 m_transforming_liquid.push_back(airs[i].p);
1828 // this flow has turned to air; neighboring flows might need to do the same
1829 for (u16 i = 0; i < num_flows; i++)
1830 m_transforming_liquid.push_back(flows[i].p);
1834 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1835 while (must_reflow.size() > 0)
1836 m_transforming_liquid.push_back(must_reflow.pop_front());
1837 updateLighting(lighting_modified_blocks, modified_blocks);
1840 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1842 v3s16 blockpos = getNodeBlockPos(p);
1843 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 infostream<<"Map::getNodeMetadata(): Need to emerge "
1847 <<PP(blockpos)<<std::endl;
1848 block = emergeBlock(blockpos, false);
1852 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1856 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1860 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1862 v3s16 blockpos = getNodeBlockPos(p);
1863 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1864 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1866 infostream<<"Map::setNodeMetadata(): Need to emerge "
1867 <<PP(blockpos)<<std::endl;
1868 block = emergeBlock(blockpos, false);
1872 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1876 block->m_node_metadata->set(p_rel, meta);
1879 void Map::removeNodeMetadata(v3s16 p)
1881 v3s16 blockpos = getNodeBlockPos(p);
1882 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1883 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1886 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1890 block->m_node_metadata->remove(p_rel);
1893 void Map::nodeMetadataStep(float dtime,
1894 core::map<v3s16, MapBlock*> &changed_blocks)
1898 Currently there is no way to ensure that all the necessary
1899 blocks are loaded when this is run. (They might get unloaded)
1900 NOTE: ^- Actually, that might not be so. In a quick test it
1901 reloaded a block with a furnace when I walked back to it from
1904 core::map<v2s16, MapSector*>::Iterator si;
1905 si = m_sectors.getIterator();
1906 for(; si.atEnd() == false; si++)
1908 MapSector *sector = si.getNode()->getValue();
1909 core::list< MapBlock * > sectorblocks;
1910 sector->getBlocks(sectorblocks);
1911 core::list< MapBlock * >::Iterator i;
1912 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1914 MapBlock *block = *i;
1915 bool changed = block->m_node_metadata->step(dtime);
1917 changed_blocks[block->getPos()] = block;
1926 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1927 Map(dout_server, gamedef),
1929 m_map_metadata_changed(true),
1931 m_database_read(NULL),
1932 m_database_write(NULL)
1934 verbosestream<<__FUNCTION_NAME<<std::endl;
1936 //m_chunksize = 8; // Takes a few seconds
1938 if (g_settings->get("fixed_map_seed").empty())
1940 m_seed = (((u64)(myrand()%0xffff)<<0)
1941 + ((u64)(myrand()%0xffff)<<16)
1942 + ((u64)(myrand()%0xffff)<<32)
1943 + ((u64)(myrand()%0xffff)<<48));
1947 m_seed = g_settings->getU64("fixed_map_seed");
1951 Experimental and debug stuff
1958 Try to load map; if not found, create a new one.
1961 m_savedir = savedir;
1962 m_map_saving_enabled = false;
1966 // If directory exists, check contents and load if possible
1967 if(fs::PathExists(m_savedir))
1969 // If directory is empty, it is safe to save into it.
1970 if(fs::GetDirListing(m_savedir).size() == 0)
1972 infostream<<"ServerMap: Empty save directory is valid."
1974 m_map_saving_enabled = true;
1979 // Load map metadata (seed, chunksize)
1982 catch(FileNotGoodException &e){
1983 infostream<<"WARNING: Could not load map metadata"
1984 //<<" Disabling chunk-based generator."
1989 infostream<<"ServerMap: Successfully loaded map "
1990 <<"metadata from "<<savedir
1991 <<", assuming valid save directory."
1992 <<" seed="<<m_seed<<"."
1995 m_map_saving_enabled = true;
1996 // Map loaded, not creating new one
2000 // If directory doesn't exist, it is safe to save to it
2002 m_map_saving_enabled = true;
2005 catch(std::exception &e)
2007 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2008 <<", exception: "<<e.what()<<std::endl;
2009 infostream<<"Please remove the map or fix it."<<std::endl;
2010 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2013 infostream<<"Initializing new map."<<std::endl;
2015 // Create zero sector
2016 emergeSector(v2s16(0,0));
2018 // Initially write whole map
2019 save(MOD_STATE_CLEAN);
2022 ServerMap::~ServerMap()
2024 verbosestream<<__FUNCTION_NAME<<std::endl;
2028 if(m_map_saving_enabled)
2030 // Save only changed parts
2031 save(MOD_STATE_WRITE_AT_UNLOAD);
2032 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2036 infostream<<"ServerMap: Map not saved"<<std::endl;
2039 catch(std::exception &e)
2041 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2042 <<", exception: "<<e.what()<<std::endl;
2046 Close database if it was opened
2049 sqlite3_finalize(m_database_read);
2050 if(m_database_write)
2051 sqlite3_finalize(m_database_write);
2053 sqlite3_close(m_database);
2059 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2060 for(; i.atEnd() == false; i++)
2062 MapChunk *chunk = i.getNode()->getValue();
2068 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2070 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2071 if(enable_mapgen_debug_info)
2072 infostream<<"initBlockMake(): "
2073 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2074 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2078 v3s16 chunk_offset(-1,-1,-1);
2079 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2080 v3s16 blockpos_min = blockpos_div * chunksize;
2081 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2082 blockpos_min += chunk_offset;
2083 blockpos_max += chunk_offset;
2085 // Do nothing if not inside limits (+-1 because of neighbors)
2086 if(blockpos_over_limit(blockpos_min - v3s16(1,1,1)) ||
2087 blockpos_over_limit(blockpos_max + v3s16(1,1,1)))
2093 data->no_op = false;
2094 data->seed = m_seed;
2095 data->blockpos_min = blockpos_min;
2096 data->blockpos_max = blockpos_max;
2097 data->blockpos_requested = blockpos;
2098 data->nodedef = m_gamedef->ndef();
2101 Create the whole area of this and the neighboring blocks
2104 //TimeTaker timer("initBlockMake() create area");
2106 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2107 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2109 v2s16 sectorpos(x, z);
2110 // Sector metadata is loaded from disk if not already loaded.
2111 ServerMapSector *sector = createSector(sectorpos);
2114 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2117 //MapBlock *block = createBlock(p);
2118 // 1) get from memory, 2) load from disk
2119 MapBlock *block = emergeBlock(p, false);
2120 // 3) create a blank one
2123 block = createBlock(p);
2126 Block gets sunlight if this is true.
2128 Refer to the map generator heuristics.
2130 bool ug = mapgen::block_is_underground(data->seed, p);
2131 block->setIsUnderground(ug);
2134 // Lighting will not be valid after make_chunk is called
2135 block->setLightingExpired(true);
2136 // Lighting will be calculated
2137 //block->setLightingExpired(false);
2143 Now we have a big empty area.
2145 Make a ManualMapVoxelManipulator that contains this and the
2149 // The area that contains this block and it's neighbors
2150 v3s16 bigarea_blocks_min = blockpos_min - v3s16(1,1,1);
2151 v3s16 bigarea_blocks_max = blockpos_max + v3s16(1,1,1);
2153 data->vmanip = new ManualMapVoxelManipulator(this);
2154 //data->vmanip->setMap(this);
2158 //TimeTaker timer("initBlockMake() initialEmerge");
2159 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2162 // Data is ready now.
2165 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2166 core::map<v3s16, MapBlock*> &changed_blocks)
2168 v3s16 blockpos_min = data->blockpos_min;
2169 v3s16 blockpos_max = data->blockpos_max;
2170 v3s16 blockpos_requested = data->blockpos_requested;
2171 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2172 <<blockpos_requested.Y<<","
2173 <<blockpos_requested.Z<<")"<<std::endl;*/
2177 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2181 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2183 /*infostream<<"Resulting vmanip:"<<std::endl;
2184 data->vmanip.print(infostream);*/
2186 // Make sure affected blocks are loaded
2187 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2188 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2189 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2192 // Load from disk if not already in memory
2193 emergeBlock(p, false);
2197 Blit generated stuff to map
2198 NOTE: blitBackAll adds nearly everything to changed_blocks
2202 //TimeTaker timer("finishBlockMake() blitBackAll");
2203 data->vmanip->blitBackAll(&changed_blocks);
2206 if(enable_mapgen_debug_info)
2207 infostream<<"finishBlockMake: changed_blocks.size()="
2208 <<changed_blocks.size()<<std::endl;
2211 Copy transforming liquid information
2213 while(data->transforming_liquid.size() > 0)
2215 v3s16 p = data->transforming_liquid.pop_front();
2216 m_transforming_liquid.push_back(p);
2220 Do stuff in central blocks
2228 TimeTaker t("finishBlockMake lighting update");
2230 core::map<v3s16, MapBlock*> lighting_update_blocks;
2233 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2234 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2235 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2238 MapBlock *block = getBlockNoCreateNoEx(p);
2240 lighting_update_blocks.insert(block->getPos(), block);
2243 updateLighting(lighting_update_blocks, changed_blocks);
2247 Set lighting to non-expired state in all of them.
2248 This is cheating, but it is not fast enough if all of them
2249 would actually be updated.
2251 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2252 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2253 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2256 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2260 if(enable_mapgen_debug_info == false)
2261 t.stop(true); // Hide output
2266 Go through changed blocks
2268 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2269 i.atEnd() == false; i++)
2271 MapBlock *block = i.getNode()->getValue();
2274 Update day/night difference cache of the MapBlocks
2276 block->updateDayNightDiff();
2278 Set block as modified
2280 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2281 "finishBlockMake updateDayNightDiff");
2285 Set central blocks as generated
2287 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2288 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2289 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2292 MapBlock *block = getBlockNoCreateNoEx(p);
2294 block->setGenerated(true);
2298 Save changed parts of map
2299 NOTE: Will be saved later.
2301 //save(MOD_STATE_WRITE_AT_UNLOAD);
2303 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2304 <<","<<blockpos_requested.Y<<","
2305 <<blockpos_requested.Z<<")"<<std::endl;*/
2307 if(enable_mapgen_debug_info)
2310 Analyze resulting blocks
2312 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2313 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2314 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2315 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2316 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2317 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2319 v3s16 p = v3s16(x,y,z);
2320 MapBlock *block = getBlockNoCreateNoEx(p);
2322 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2323 infostream<<"Generated "<<spos<<": "
2324 <<analyze_block(block)<<std::endl;
2329 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2335 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2337 DSTACKF("%s: p2d=(%d,%d)",
2342 Check if it exists already in memory
2344 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2349 Try to load it from disk (with blocks)
2351 //if(loadSectorFull(p2d) == true)
2354 Try to load metadata from disk
2357 if(loadSectorMeta(p2d) == true)
2359 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2362 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2363 throw InvalidPositionException("");
2369 Do not create over-limit
2371 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2372 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2373 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2374 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2375 throw InvalidPositionException("createSector(): pos. over limit");
2378 Generate blank sector
2381 sector = new ServerMapSector(this, p2d, m_gamedef);
2383 // Sector position on map in nodes
2384 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2389 m_sectors.insert(p2d, sector);
2395 This is a quick-hand function for calling makeBlock().
2397 MapBlock * ServerMap::generateBlock(
2399 core::map<v3s16, MapBlock*> &modified_blocks
2402 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2404 /*infostream<<"generateBlock(): "
2405 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2408 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2410 TimeTaker timer("generateBlock");
2412 //MapBlock *block = original_dummy;
2414 v2s16 p2d(p.X, p.Z);
2415 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2418 Do not generate over-limit
2420 if(blockpos_over_limit(p))
2422 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2423 throw InvalidPositionException("generateBlock(): pos. over limit");
2427 Create block make data
2429 mapgen::BlockMakeData data;
2430 initBlockMake(&data, p);
2436 TimeTaker t("mapgen::make_block()");
2437 mapgen::make_block(&data);
2439 if(enable_mapgen_debug_info == false)
2440 t.stop(true); // Hide output
2444 Blit data back on map, update lighting, add mobs and whatever this does
2446 finishBlockMake(&data, modified_blocks);
2451 MapBlock *block = getBlockNoCreateNoEx(p);
2459 bool erroneus_content = false;
2460 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2461 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2462 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2465 MapNode n = block->getNode(p);
2466 if(n.getContent() == CONTENT_IGNORE)
2468 infostream<<"CONTENT_IGNORE at "
2469 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2471 erroneus_content = true;
2475 if(erroneus_content)
2484 Generate a completely empty block
2488 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2489 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2491 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2494 n.setContent(CONTENT_AIR);
2495 block->setNode(v3s16(x0,y0,z0), n);
2501 if(enable_mapgen_debug_info == false)
2502 timer.stop(true); // Hide output
2507 MapBlock * ServerMap::createBlock(v3s16 p)
2509 DSTACKF("%s: p=(%d,%d,%d)",
2510 __FUNCTION_NAME, p.X, p.Y, p.Z);
2513 Do not create over-limit
2515 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2517 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2518 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2519 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2520 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2521 throw InvalidPositionException("createBlock(): pos. over limit");
2523 v2s16 p2d(p.X, p.Z);
2526 This will create or load a sector if not found in memory.
2527 If block exists on disk, it will be loaded.
2529 NOTE: On old save formats, this will be slow, as it generates
2530 lighting on blocks for them.
2532 ServerMapSector *sector;
2534 sector = (ServerMapSector*)createSector(p2d);
2535 assert(sector->getId() == MAPSECTOR_SERVER);
2537 catch(InvalidPositionException &e)
2539 infostream<<"createBlock: createSector() failed"<<std::endl;
2543 NOTE: This should not be done, or at least the exception
2544 should not be passed on as std::exception, because it
2545 won't be catched at all.
2547 /*catch(std::exception &e)
2549 infostream<<"createBlock: createSector() failed: "
2550 <<e.what()<<std::endl;
2555 Try to get a block from the sector
2558 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2561 if(block->isDummy())
2566 block = sector->createBlankBlock(block_y);
2570 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2572 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2574 p.X, p.Y, p.Z, allow_generate);
2577 MapBlock *block = getBlockNoCreateNoEx(p);
2578 if(block && block->isDummy() == false)
2583 MapBlock *block = loadBlock(p);
2590 core::map<v3s16, MapBlock*> modified_blocks;
2591 MapBlock *block = generateBlock(p, modified_blocks);
2595 event.type = MEET_OTHER;
2598 // Copy modified_blocks to event
2599 for(core::map<v3s16, MapBlock*>::Iterator
2600 i = modified_blocks.getIterator();
2601 i.atEnd()==false; i++)
2603 event.modified_blocks.insert(i.getNode()->getKey(), false);
2607 dispatchEvent(&event);
2616 s16 ServerMap::findGroundLevel(v2s16 p2d)
2620 Uh, just do something random...
2622 // Find existing map from top to down
2625 v3s16 p(p2d.X, max, p2d.Y);
2626 for(; p.Y>min; p.Y--)
2628 MapNode n = getNodeNoEx(p);
2629 if(n.getContent() != CONTENT_IGNORE)
2634 // If this node is not air, go to plan b
2635 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2637 // Search existing walkable and return it
2638 for(; p.Y>min; p.Y--)
2640 MapNode n = getNodeNoEx(p);
2641 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2650 Determine from map generator noise functions
2653 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2656 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2657 //return (s16)level;
2660 void ServerMap::createDatabase() {
2663 e = sqlite3_exec(m_database,
2664 "CREATE TABLE IF NOT EXISTS `blocks` ("
2665 "`pos` INT NOT NULL PRIMARY KEY,"
2668 , NULL, NULL, NULL);
2669 if(e == SQLITE_ABORT)
2670 throw FileNotGoodException("Could not create database structure");
2672 infostream<<"ServerMap: Database structure was created";
2675 void ServerMap::verifyDatabase() {
2680 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2681 bool needs_create = false;
2685 Open the database connection
2688 createDirs(m_savedir);
2690 if(!fs::PathExists(dbp))
2691 needs_create = true;
2693 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2694 if(d != SQLITE_OK) {
2695 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2696 throw FileNotGoodException("Cannot open database file");
2702 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2703 if(d != SQLITE_OK) {
2704 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2705 throw FileNotGoodException("Cannot prepare read statement");
2708 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2709 if(d != SQLITE_OK) {
2710 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2711 throw FileNotGoodException("Cannot prepare write statement");
2714 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2715 if(d != SQLITE_OK) {
2716 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2717 throw FileNotGoodException("Cannot prepare read statement");
2720 infostream<<"ServerMap: Database opened"<<std::endl;
2724 bool ServerMap::loadFromFolders() {
2725 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2730 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2731 return (sqlite3_int64)pos.Z*16777216 +
2732 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2735 void ServerMap::createDirs(std::string path)
2737 if(fs::CreateAllDirs(path) == false)
2739 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2740 <<"\""<<path<<"\""<<std::endl;
2741 throw BaseException("ServerMap failed to create directory");
2745 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2751 snprintf(cc, 9, "%.4x%.4x",
2752 (unsigned int)pos.X&0xffff,
2753 (unsigned int)pos.Y&0xffff);
2755 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2757 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2758 (unsigned int)pos.X&0xfff,
2759 (unsigned int)pos.Y&0xfff);
2761 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2767 v2s16 ServerMap::getSectorPos(std::string dirname)
2771 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2772 assert(spos != std::string::npos);
2773 if(dirname.size() - spos == 8)
2776 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2778 else if(dirname.size() - spos == 3)
2781 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2782 // Sign-extend the 12 bit values up to 16 bits...
2783 if(x&0x800) x|=0xF000;
2784 if(y&0x800) y|=0xF000;
2791 v2s16 pos((s16)x, (s16)y);
2795 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2797 v2s16 p2d = getSectorPos(sectordir);
2799 if(blockfile.size() != 4){
2800 throw InvalidFilenameException("Invalid block filename");
2803 int r = sscanf(blockfile.c_str(), "%4x", &y);
2805 throw InvalidFilenameException("Invalid block filename");
2806 return v3s16(p2d.X, y, p2d.Y);
2809 std::string ServerMap::getBlockFilename(v3s16 p)
2812 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2816 void ServerMap::save(ModifiedState save_level)
2818 DSTACK(__FUNCTION_NAME);
2819 if(m_map_saving_enabled == false)
2821 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2825 if(save_level == MOD_STATE_CLEAN)
2826 infostream<<"ServerMap: Saving whole map, this can take time."
2829 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2834 // Profile modified reasons
2835 Profiler modprofiler;
2837 u32 sector_meta_count = 0;
2838 u32 block_count = 0;
2839 u32 block_count_all = 0; // Number of blocks in memory
2841 // Don't do anything with sqlite unless something is really saved
2842 bool save_started = false;
2844 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2845 for(; i.atEnd() == false; i++)
2847 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2848 assert(sector->getId() == MAPSECTOR_SERVER);
2850 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2852 saveSectorMeta(sector);
2853 sector_meta_count++;
2855 core::list<MapBlock*> blocks;
2856 sector->getBlocks(blocks);
2857 core::list<MapBlock*>::Iterator j;
2859 for(j=blocks.begin(); j!=blocks.end(); j++)
2861 MapBlock *block = *j;
2865 if(block->getModified() >= save_level)
2870 save_started = true;
2873 modprofiler.add(block->getModifiedReason(), 1);
2878 /*infostream<<"ServerMap: Written block ("
2879 <<block->getPos().X<<","
2880 <<block->getPos().Y<<","
2881 <<block->getPos().Z<<")"
2890 Only print if something happened or saved whole map
2892 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2893 || block_count != 0)
2895 infostream<<"ServerMap: Written: "
2896 <<sector_meta_count<<" sector metadata files, "
2897 <<block_count<<" block files"
2898 <<", "<<block_count_all<<" blocks in memory."
2900 PrintInfo(infostream); // ServerMap/ClientMap:
2901 infostream<<"Blocks modified by: "<<std::endl;
2902 modprofiler.print(infostream);
2906 static s32 unsignedToSigned(s32 i, s32 max_positive)
2908 if(i < max_positive)
2911 return i - 2*max_positive;
2914 // modulo of a negative number does not work consistently in C
2915 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2919 return mod - ((-i) % mod);
2922 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2924 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2926 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2928 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2929 return v3s16(x,y,z);
2932 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2934 if(loadFromFolders()){
2935 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2936 <<"all blocks that are stored in flat files"<<std::endl;
2942 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2944 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2945 v3s16 p = getIntegerAsBlock(block_i);
2946 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2952 void ServerMap::saveMapMeta()
2954 DSTACK(__FUNCTION_NAME);
2956 /*infostream<<"ServerMap::saveMapMeta(): "
2960 createDirs(m_savedir);
2962 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2963 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2964 if(os.good() == false)
2966 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2967 <<"could not open"<<fullpath<<std::endl;
2968 throw FileNotGoodException("Cannot open chunk metadata");
2972 params.setU64("seed", m_seed);
2974 params.writeLines(os);
2976 os<<"[end_of_params]\n";
2978 m_map_metadata_changed = false;
2981 void ServerMap::loadMapMeta()
2983 DSTACK(__FUNCTION_NAME);
2985 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2988 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2989 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2990 if(is.good() == false)
2992 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2993 <<"could not open"<<fullpath<<std::endl;
2994 throw FileNotGoodException("Cannot open map metadata");
3002 throw SerializationError
3003 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3005 std::getline(is, line);
3006 std::string trimmedline = trim(line);
3007 if(trimmedline == "[end_of_params]")
3009 params.parseConfigLine(line);
3012 m_seed = params.getU64("seed");
3014 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3017 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3019 DSTACK(__FUNCTION_NAME);
3020 // Format used for writing
3021 u8 version = SER_FMT_VER_HIGHEST;
3023 v2s16 pos = sector->getPos();
3024 std::string dir = getSectorDir(pos);
3027 std::string fullpath = dir + DIR_DELIM + "meta";
3028 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3029 if(o.good() == false)
3030 throw FileNotGoodException("Cannot open sector metafile");
3032 sector->serialize(o, version);
3034 sector->differs_from_disk = false;
3037 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3039 DSTACK(__FUNCTION_NAME);
3041 v2s16 p2d = getSectorPos(sectordir);
3043 ServerMapSector *sector = NULL;
3045 std::string fullpath = sectordir + DIR_DELIM + "meta";
3046 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3047 if(is.good() == false)
3049 // If the directory exists anyway, it probably is in some old
3050 // format. Just go ahead and create the sector.
3051 if(fs::PathExists(sectordir))
3053 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3054 <<fullpath<<" doesn't exist but directory does."
3055 <<" Continuing with a sector with no metadata."
3057 sector = new ServerMapSector(this, p2d, m_gamedef);
3058 m_sectors.insert(p2d, sector);
3062 throw FileNotGoodException("Cannot open sector metafile");
3067 sector = ServerMapSector::deSerialize
3068 (is, this, p2d, m_sectors, m_gamedef);
3070 saveSectorMeta(sector);
3073 sector->differs_from_disk = false;
3078 bool ServerMap::loadSectorMeta(v2s16 p2d)
3080 DSTACK(__FUNCTION_NAME);
3082 MapSector *sector = NULL;
3084 // The directory layout we're going to load from.
3085 // 1 - original sectors/xxxxzzzz/
3086 // 2 - new sectors2/xxx/zzz/
3087 // If we load from anything but the latest structure, we will
3088 // immediately save to the new one, and remove the old.
3090 std::string sectordir1 = getSectorDir(p2d, 1);
3091 std::string sectordir;
3092 if(fs::PathExists(sectordir1))
3094 sectordir = sectordir1;
3099 sectordir = getSectorDir(p2d, 2);
3103 sector = loadSectorMeta(sectordir, loadlayout != 2);
3105 catch(InvalidFilenameException &e)
3109 catch(FileNotGoodException &e)
3113 catch(std::exception &e)
3122 bool ServerMap::loadSectorFull(v2s16 p2d)
3124 DSTACK(__FUNCTION_NAME);
3126 MapSector *sector = NULL;
3128 // The directory layout we're going to load from.
3129 // 1 - original sectors/xxxxzzzz/
3130 // 2 - new sectors2/xxx/zzz/
3131 // If we load from anything but the latest structure, we will
3132 // immediately save to the new one, and remove the old.
3134 std::string sectordir1 = getSectorDir(p2d, 1);
3135 std::string sectordir;
3136 if(fs::PathExists(sectordir1))
3138 sectordir = sectordir1;
3143 sectordir = getSectorDir(p2d, 2);
3147 sector = loadSectorMeta(sectordir, loadlayout != 2);
3149 catch(InvalidFilenameException &e)
3153 catch(FileNotGoodException &e)
3157 catch(std::exception &e)
3165 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3167 std::vector<fs::DirListNode>::iterator i2;
3168 for(i2=list2.begin(); i2!=list2.end(); i2++)
3174 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3176 catch(InvalidFilenameException &e)
3178 // This catches unknown crap in directory
3184 infostream<<"Sector converted to new layout - deleting "<<
3185 sectordir1<<std::endl;
3186 fs::RecursiveDelete(sectordir1);
3193 void ServerMap::beginSave() {
3195 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3196 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3199 void ServerMap::endSave() {
3201 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3202 infostream<<"WARNING: endSave() failed, map might not have saved.";
3205 void ServerMap::saveBlock(MapBlock *block)
3207 DSTACK(__FUNCTION_NAME);
3209 Dummy blocks are not written
3211 if(block->isDummy())
3213 /*v3s16 p = block->getPos();
3214 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3215 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3219 // Format used for writing
3220 u8 version = SER_FMT_VER_HIGHEST;
3222 v3s16 p3d = block->getPos();
3226 v2s16 p2d(p3d.X, p3d.Z);
3227 std::string sectordir = getSectorDir(p2d);
3229 createDirs(sectordir);
3231 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3232 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3233 if(o.good() == false)
3234 throw FileNotGoodException("Cannot open block data");
3237 [0] u8 serialization version
3243 std::ostringstream o(std::ios_base::binary);
3245 o.write((char*)&version, 1);
3248 block->serialize(o, version, true);
3250 // Write block to database
3252 std::string tmp = o.str();
3253 const char *bytes = tmp.c_str();
3255 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3256 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3257 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3258 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3259 int written = sqlite3_step(m_database_write);
3260 if(written != SQLITE_DONE)
3261 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3262 <<sqlite3_errmsg(m_database)<<std::endl;
3263 // Make ready for later reuse
3264 sqlite3_reset(m_database_write);
3266 // We just wrote it to the disk so clear modified flag
3267 block->resetModified();
3270 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3272 DSTACK(__FUNCTION_NAME);
3274 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3277 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3278 if(is.good() == false)
3279 throw FileNotGoodException("Cannot open block file");
3281 v3s16 p3d = getBlockPos(sectordir, blockfile);
3282 v2s16 p2d(p3d.X, p3d.Z);
3284 assert(sector->getPos() == p2d);
3286 u8 version = SER_FMT_VER_INVALID;
3287 is.read((char*)&version, 1);
3290 throw SerializationError("ServerMap::loadBlock(): Failed"
3291 " to read MapBlock version");
3293 /*u32 block_size = MapBlock::serializedLength(version);
3294 SharedBuffer<u8> data(block_size);
3295 is.read((char*)*data, block_size);*/
3297 // This will always return a sector because we're the server
3298 //MapSector *sector = emergeSector(p2d);
3300 MapBlock *block = NULL;
3301 bool created_new = false;
3302 block = sector->getBlockNoCreateNoEx(p3d.Y);
3305 block = sector->createBlankBlockNoInsert(p3d.Y);
3310 block->deSerialize(is, version, true);
3312 // If it's a new block, insert it to the map
3314 sector->insertBlock(block);
3317 Save blocks loaded in old format in new format
3320 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3324 // Should be in database now, so delete the old file
3325 fs::RecursiveDelete(fullpath);
3328 // We just loaded it from the disk, so it's up-to-date.
3329 block->resetModified();
3332 catch(SerializationError &e)
3334 infostream<<"WARNING: Invalid block data on disk "
3335 <<"fullpath="<<fullpath
3336 <<" (SerializationError). "
3337 <<"what()="<<e.what()
3339 //" Ignoring. A new one will be generated.
3342 // TODO: Backup file; name is in fullpath.
3346 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3348 DSTACK(__FUNCTION_NAME);
3351 std::istringstream is(*blob, std::ios_base::binary);
3353 u8 version = SER_FMT_VER_INVALID;
3354 is.read((char*)&version, 1);
3357 throw SerializationError("ServerMap::loadBlock(): Failed"
3358 " to read MapBlock version");
3360 /*u32 block_size = MapBlock::serializedLength(version);
3361 SharedBuffer<u8> data(block_size);
3362 is.read((char*)*data, block_size);*/
3364 // This will always return a sector because we're the server
3365 //MapSector *sector = emergeSector(p2d);
3367 MapBlock *block = NULL;
3368 bool created_new = false;
3369 block = sector->getBlockNoCreateNoEx(p3d.Y);
3372 block = sector->createBlankBlockNoInsert(p3d.Y);
3377 block->deSerialize(is, version, true);
3379 // If it's a new block, insert it to the map
3381 sector->insertBlock(block);
3384 Save blocks loaded in old format in new format
3387 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3388 // Only save if asked to; no need to update version
3392 // We just loaded it from, so it's up-to-date.
3393 block->resetModified();
3396 catch(SerializationError &e)
3398 infostream<<"WARNING: Invalid block data in database "
3399 <<" (SerializationError). "
3400 <<"what()="<<e.what()
3402 //" Ignoring. A new one will be generated.
3405 // TODO: Copy to a backup database.
3409 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3411 DSTACK(__FUNCTION_NAME);
3413 v2s16 p2d(blockpos.X, blockpos.Z);
3415 if(!loadFromFolders()) {
3418 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3419 infostream<<"WARNING: Could not bind block position for load: "
3420 <<sqlite3_errmsg(m_database)<<std::endl;
3421 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3423 Make sure sector is loaded
3425 MapSector *sector = createSector(p2d);
3430 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3431 size_t len = sqlite3_column_bytes(m_database_read, 0);
3433 std::string datastr(data, len);
3435 loadBlock(&datastr, blockpos, sector, false);
3437 sqlite3_step(m_database_read);
3438 // We should never get more than 1 row, so ok to reset
3439 sqlite3_reset(m_database_read);
3441 return getBlockNoCreateNoEx(blockpos);
3443 sqlite3_reset(m_database_read);
3445 // Not found in database, try the files
3448 // The directory layout we're going to load from.
3449 // 1 - original sectors/xxxxzzzz/
3450 // 2 - new sectors2/xxx/zzz/
3451 // If we load from anything but the latest structure, we will
3452 // immediately save to the new one, and remove the old.
3454 std::string sectordir1 = getSectorDir(p2d, 1);
3455 std::string sectordir;
3456 if(fs::PathExists(sectordir1))
3458 sectordir = sectordir1;
3463 sectordir = getSectorDir(p2d, 2);
3467 Make sure sector is loaded
3469 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3473 sector = loadSectorMeta(sectordir, loadlayout != 2);
3475 catch(InvalidFilenameException &e)
3479 catch(FileNotGoodException &e)
3483 catch(std::exception &e)
3490 Make sure file exists
3493 std::string blockfilename = getBlockFilename(blockpos);
3494 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3498 Load block and save it to the database
3500 loadBlock(sectordir, blockfilename, sector, true);
3501 return getBlockNoCreateNoEx(blockpos);
3504 void ServerMap::PrintInfo(std::ostream &out)
3513 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3518 MapVoxelManipulator::~MapVoxelManipulator()
3520 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3524 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3526 TimeTaker timer1("emerge", &emerge_time);
3528 // Units of these are MapBlocks
3529 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3530 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3532 VoxelArea block_area_nodes
3533 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3535 addArea(block_area_nodes);
3537 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3538 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3539 for(s32 x=p_min.X; x<=p_max.X; x++)
3542 core::map<v3s16, bool>::Node *n;
3543 n = m_loaded_blocks.find(p);
3547 bool block_data_inexistent = false;
3550 TimeTaker timer1("emerge load", &emerge_load_time);
3552 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3553 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3555 a.print(infostream);
3556 infostream<<std::endl;*/
3558 MapBlock *block = m_map->getBlockNoCreate(p);
3559 if(block->isDummy())
3560 block_data_inexistent = true;
3562 block->copyTo(*this);
3564 catch(InvalidPositionException &e)
3566 block_data_inexistent = true;
3569 if(block_data_inexistent)
3571 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3572 // Fill with VOXELFLAG_INEXISTENT
3573 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3574 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3576 s32 i = m_area.index(a.MinEdge.X,y,z);
3577 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3581 m_loaded_blocks.insert(p, !block_data_inexistent);
3584 //infostream<<"emerge done"<<std::endl;
3588 SUGG: Add an option to only update eg. water and air nodes.
3589 This will make it interfere less with important stuff if
3592 void MapVoxelManipulator::blitBack
3593 (core::map<v3s16, MapBlock*> & modified_blocks)
3595 if(m_area.getExtent() == v3s16(0,0,0))
3598 //TimeTaker timer1("blitBack");
3600 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3601 <<m_loaded_blocks.size()<<std::endl;*/
3604 Initialize block cache
3606 v3s16 blockpos_last;
3607 MapBlock *block = NULL;
3608 bool block_checked_in_modified = false;
3610 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3611 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3612 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3616 u8 f = m_flags[m_area.index(p)];
3617 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3620 MapNode &n = m_data[m_area.index(p)];
3622 v3s16 blockpos = getNodeBlockPos(p);
3627 if(block == NULL || blockpos != blockpos_last){
3628 block = m_map->getBlockNoCreate(blockpos);
3629 blockpos_last = blockpos;
3630 block_checked_in_modified = false;
3633 // Calculate relative position in block
3634 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3636 // Don't continue if nothing has changed here
3637 if(block->getNode(relpos) == n)
3640 //m_map->setNode(m_area.MinEdge + p, n);
3641 block->setNode(relpos, n);
3644 Make sure block is in modified_blocks
3646 if(block_checked_in_modified == false)
3648 modified_blocks[blockpos] = block;
3649 block_checked_in_modified = true;
3652 catch(InvalidPositionException &e)
3658 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3659 MapVoxelManipulator(map),
3660 m_create_area(false)
3664 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3668 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3670 // Just create the area so that it can be pointed to
3671 VoxelManipulator::emerge(a, caller_id);
3674 void ManualMapVoxelManipulator::initialEmerge(
3675 v3s16 blockpos_min, v3s16 blockpos_max)
3677 TimeTaker timer1("initialEmerge", &emerge_time);
3679 // Units of these are MapBlocks
3680 v3s16 p_min = blockpos_min;
3681 v3s16 p_max = blockpos_max;
3683 VoxelArea block_area_nodes
3684 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3686 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3689 infostream<<"initialEmerge: area: ";
3690 block_area_nodes.print(infostream);
3691 infostream<<" ("<<size_MB<<"MB)";
3692 infostream<<std::endl;
3695 addArea(block_area_nodes);
3697 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3698 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3699 for(s32 x=p_min.X; x<=p_max.X; x++)
3702 core::map<v3s16, bool>::Node *n;
3703 n = m_loaded_blocks.find(p);
3707 bool block_data_inexistent = false;
3710 TimeTaker timer1("emerge load", &emerge_load_time);
3712 MapBlock *block = m_map->getBlockNoCreate(p);
3713 if(block->isDummy())
3714 block_data_inexistent = true;
3716 block->copyTo(*this);
3718 catch(InvalidPositionException &e)
3720 block_data_inexistent = true;
3723 if(block_data_inexistent)
3726 Mark area inexistent
3728 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3729 // Fill with VOXELFLAG_INEXISTENT
3730 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3731 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3733 s32 i = m_area.index(a.MinEdge.X,y,z);
3734 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3738 m_loaded_blocks.insert(p, !block_data_inexistent);
3742 void ManualMapVoxelManipulator::blitBackAll(
3743 core::map<v3s16, MapBlock*> * modified_blocks)
3745 if(m_area.getExtent() == v3s16(0,0,0))
3749 Copy data of all blocks
3751 for(core::map<v3s16, bool>::Iterator
3752 i = m_loaded_blocks.getIterator();
3753 i.atEnd() == false; i++)
3755 v3s16 p = i.getNode()->getKey();
3756 bool existed = i.getNode()->getValue();
3757 if(existed == false)
3759 // The Great Bug was found using this
3760 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3761 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3765 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3768 infostream<<"WARNING: "<<__FUNCTION_NAME
3769 <<": got NULL block "
3770 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3775 block->copyFrom(*this);
3778 modified_blocks->insert(p, block);