3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
29 #include "nodemetadata.h"
36 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
42 If map.sqlite does not exist in the save dir
43 or the block was not found in the database
44 the map will try to load from sectors folder.
45 In either case, map.sqlite will be created
46 and all future saves will save there.
48 Structure of map.sqlite:
59 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 /*m_sector_mutex.Init();
65 assert(m_sector_mutex.IsInitialized());*/
73 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
74 for(; i.atEnd() == false; i++)
76 MapSector *sector = i.getNode()->getValue();
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver, false);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 if(m_event_receivers.find(event_receiver) == NULL)
90 m_event_receivers.remove(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(core::map<MapEventReceiver*, bool>::Iterator
96 i = m_event_receivers.getIterator();
97 i.atEnd()==false; i++)
99 MapEventReceiver* event_receiver = i.getNode()->getKey();
100 event_receiver->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
116 MapSector *sector = n->getValue();
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 core::map<v3s16, u8> & from_nodes,
237 core::map<v3s16, bool> & light_sources,
238 core::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 core::map<v3s16, u8> unlighted_nodes;
257 core::map<v3s16, u8>::Iterator j;
258 j = from_nodes.getIterator();
261 Initialize block cache
264 MapBlock *block = NULL;
265 // Cache this a bit, too
266 bool block_checked_in_modified = false;
268 for(; j.atEnd() == false; j++)
270 v3s16 pos = j.getNode()->getKey();
271 v3s16 blockpos = getNodeBlockPos(pos);
273 // Only fetch a new block if the block position has changed
275 if(block == NULL || blockpos != blockpos_last){
276 block = getBlockNoCreate(blockpos);
277 blockpos_last = blockpos;
279 block_checked_in_modified = false;
283 catch(InvalidPositionException &e)
291 // Calculate relative position in block
292 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
294 // Get node straight from the block
295 MapNode n = block->getNode(relpos);
297 u8 oldlight = j.getNode()->getValue();
299 // Loop through 6 neighbors
300 for(u16 i=0; i<6; i++)
302 // Get the position of the neighbor node
303 v3s16 n2pos = pos + dirs[i];
305 // Get the block where the node is located
306 v3s16 blockpos = getNodeBlockPos(n2pos);
310 // Only fetch a new block if the block position has changed
312 if(block == NULL || blockpos != blockpos_last){
313 block = getBlockNoCreate(blockpos);
314 blockpos_last = blockpos;
316 block_checked_in_modified = false;
320 catch(InvalidPositionException &e)
325 // Calculate relative position in block
326 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
327 // Get node straight from the block
328 MapNode n2 = block->getNode(relpos);
330 bool changed = false;
332 //TODO: Optimize output by optimizing light_sources?
335 If the neighbor is dimmer than what was specified
336 as oldlight (the light of the previous node)
338 if(n2.getLight(bank, nodemgr) < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates
344 && n2.getLight(bank, nodemgr) != 0)
347 Set light to 0 and add to queue
350 u8 current_light = n2.getLight(bank, nodemgr);
351 n2.setLight(bank, 0, nodemgr);
352 block->setNode(relpos, n2);
354 unlighted_nodes.insert(n2pos, current_light);
358 Remove from light_sources if it is there
359 NOTE: This doesn't happen nearly at all
361 /*if(light_sources.find(n2pos))
363 infostream<<"Removed from light_sources"<<std::endl;
364 light_sources.remove(n2pos);
369 if(light_sources.find(n2pos) != NULL)
370 light_sources.remove(n2pos);*/
373 light_sources.insert(n2pos, true);
376 // Add to modified_blocks
377 if(changed == true && block_checked_in_modified == false)
379 // If the block is not found in modified_blocks, add.
380 if(modified_blocks.find(blockpos) == NULL)
382 modified_blocks.insert(blockpos, block);
384 block_checked_in_modified = true;
387 catch(InvalidPositionException &e)
394 /*infostream<<"unspreadLight(): Changed block "
395 <<blockchangecount<<" times"
396 <<" for "<<from_nodes.size()<<" nodes"
399 if(unlighted_nodes.size() > 0)
400 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
404 A single-node wrapper of the above
406 void Map::unLightNeighbors(enum LightBank bank,
407 v3s16 pos, u8 lightwas,
408 core::map<v3s16, bool> & light_sources,
409 core::map<v3s16, MapBlock*> & modified_blocks)
411 core::map<v3s16, u8> from_nodes;
412 from_nodes.insert(pos, lightwas);
414 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 core::map<v3s16, bool> & from_nodes,
423 core::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.size() == 0)
439 u32 blockchangecount = 0;
441 core::map<v3s16, bool> lighted_nodes;
442 core::map<v3s16, bool>::Iterator j;
443 j = from_nodes.getIterator();
446 Initialize block cache
449 MapBlock *block = NULL;
450 // Cache this a bit, too
451 bool block_checked_in_modified = false;
453 for(; j.atEnd() == false; j++)
454 //for(; j != from_nodes.end(); j++)
456 v3s16 pos = j.getNode()->getKey();
458 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
459 v3s16 blockpos = getNodeBlockPos(pos);
461 // Only fetch a new block if the block position has changed
463 if(block == NULL || blockpos != blockpos_last){
464 block = getBlockNoCreate(blockpos);
465 blockpos_last = blockpos;
467 block_checked_in_modified = false;
471 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
482 // Get node straight from the block
483 MapNode n = block->getNode(relpos);
485 u8 oldlight = n.getLight(bank, nodemgr);
486 u8 newlight = diminish_light(oldlight);
488 // Loop through 6 neighbors
489 for(u16 i=0; i<6; i++){
490 // Get the position of the neighbor node
491 v3s16 n2pos = pos + dirs[i];
493 // Get the block where the node is located
494 v3s16 blockpos = getNodeBlockPos(n2pos);
498 // Only fetch a new block if the block position has changed
500 if(block == NULL || blockpos != blockpos_last){
501 block = getBlockNoCreate(blockpos);
502 blockpos_last = blockpos;
504 block_checked_in_modified = false;
508 catch(InvalidPositionException &e)
513 // Calculate relative position in block
514 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos);
518 bool changed = false;
520 If the neighbor is brighter than the current node,
521 add to list (it will light up this node on its turn)
523 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
525 lighted_nodes.insert(n2pos, true);
526 //lighted_nodes.push_back(n2pos);
530 If the neighbor is dimmer than how much light this node
531 would spread on it, add to list
533 if(n2.getLight(bank, nodemgr) < newlight)
535 if(nodemgr->get(n2).light_propagates)
537 n2.setLight(bank, newlight, nodemgr);
538 block->setNode(relpos, n2);
539 lighted_nodes.insert(n2pos, true);
540 //lighted_nodes.push_back(n2pos);
545 // Add to modified_blocks
546 if(changed == true && block_checked_in_modified == false)
548 // If the block is not found in modified_blocks, add.
549 if(modified_blocks.find(blockpos) == NULL)
551 modified_blocks.insert(blockpos, block);
553 block_checked_in_modified = true;
556 catch(InvalidPositionException &e)
563 /*infostream<<"spreadLight(): Changed block "
564 <<blockchangecount<<" times"
565 <<" for "<<from_nodes.size()<<" nodes"
568 if(lighted_nodes.size() > 0)
569 spreadLight(bank, lighted_nodes, modified_blocks);
573 A single-node source variation of the above.
575 void Map::lightNeighbors(enum LightBank bank,
577 core::map<v3s16, MapBlock*> & modified_blocks)
579 core::map<v3s16, bool> from_nodes;
580 from_nodes.insert(pos, true);
581 spreadLight(bank, from_nodes, modified_blocks);
584 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
586 INodeDefManager *nodemgr = m_gamedef->ndef();
589 v3s16(0,0,1), // back
591 v3s16(1,0,0), // right
592 v3s16(0,0,-1), // front
593 v3s16(0,-1,0), // bottom
594 v3s16(-1,0,0), // left
597 u8 brightest_light = 0;
598 v3s16 brightest_pos(0,0,0);
599 bool found_something = false;
601 // Loop through 6 neighbors
602 for(u16 i=0; i<6; i++){
603 // Get the position of the neighbor node
604 v3s16 n2pos = p + dirs[i];
609 catch(InvalidPositionException &e)
613 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
614 brightest_light = n2.getLight(bank, nodemgr);
615 brightest_pos = n2pos;
616 found_something = true;
620 if(found_something == false)
621 throw InvalidPositionException();
623 return brightest_pos;
627 Propagates sunlight down from a node.
628 Starting point gets sunlight.
630 Returns the lowest y value of where the sunlight went.
632 Mud is turned into grass in where the sunlight stops.
634 s16 Map::propagateSunlight(v3s16 start,
635 core::map<v3s16, MapBlock*> & modified_blocks)
637 INodeDefManager *nodemgr = m_gamedef->ndef();
642 v3s16 pos(start.X, y, start.Z);
644 v3s16 blockpos = getNodeBlockPos(pos);
647 block = getBlockNoCreate(blockpos);
649 catch(InvalidPositionException &e)
654 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
655 MapNode n = block->getNode(relpos);
657 if(nodemgr->get(n).sunlight_propagates)
659 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660 block->setNode(relpos, n);
662 modified_blocks.insert(blockpos, block);
666 // Sunlight goes no further
673 void Map::updateLighting(enum LightBank bank,
674 core::map<v3s16, MapBlock*> & a_blocks,
675 core::map<v3s16, MapBlock*> & modified_blocks)
677 INodeDefManager *nodemgr = m_gamedef->ndef();
679 /*m_dout<<DTIME<<"Map::updateLighting(): "
680 <<a_blocks.size()<<" blocks."<<std::endl;*/
682 //TimeTaker timer("updateLighting");
686 //u32 count_was = modified_blocks.size();
688 core::map<v3s16, MapBlock*> blocks_to_update;
690 core::map<v3s16, bool> light_sources;
692 core::map<v3s16, u8> unlight_from;
695 //TimeTaker t("first stuff");
697 core::map<v3s16, MapBlock*>::Iterator i;
698 i = a_blocks.getIterator();
699 for(; i.atEnd() == false; i++)
701 MapBlock *block = i.getNode()->getValue();
705 // Don't bother with dummy blocks.
709 v3s16 pos = block->getPos();
710 v3s16 posnodes = block->getPosRelative();
711 modified_blocks.insert(pos, block);
713 blocks_to_update.insert(pos, block);
716 Clear all light from block
718 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
719 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
720 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
725 MapNode n = block->getNode(p);
726 u8 oldlight = n.getLight(bank, nodemgr);
727 n.setLight(bank, 0, nodemgr);
728 block->setNode(p, n);
730 // Collect borders for unlighting
731 if(x==0 || x == MAP_BLOCKSIZE-1
732 || y==0 || y == MAP_BLOCKSIZE-1
733 || z==0 || z == MAP_BLOCKSIZE-1)
735 v3s16 p_map = p + posnodes;
736 unlight_from.insert(p_map, oldlight);
739 catch(InvalidPositionException &e)
742 This would happen when dealing with a
746 infostream<<"updateLighting(): InvalidPositionException"
751 if(bank == LIGHTBANK_DAY)
753 bool bottom_valid = block->propagateSunlight(light_sources);
755 // If bottom is valid, we're done.
759 else if(bank == LIGHTBANK_NIGHT)
761 // For night lighting, sunlight is not propagated
766 // Invalid lighting bank
770 /*infostream<<"Bottom for sunlight-propagated block ("
771 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
774 // Bottom sunlight is not valid; get the block and loop to it
778 block = getBlockNoCreate(pos);
780 catch(InvalidPositionException &e)
791 Enable this to disable proper lighting for speeding up map
792 generation for testing or whatever
795 //if(g_settings->get(""))
797 core::map<v3s16, MapBlock*>::Iterator i;
798 i = blocks_to_update.getIterator();
799 for(; i.atEnd() == false; i++)
801 MapBlock *block = i.getNode()->getValue();
802 v3s16 p = block->getPos();
803 block->setLightingExpired(false);
811 TimeTaker timer("unspreadLight");
812 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
817 u32 diff = modified_blocks.size() - count_was;
818 count_was = modified_blocks.size();
819 infostream<<"unspreadLight modified "<<diff<<std::endl;
823 TimeTaker timer("spreadLight");
824 spreadLight(bank, light_sources, modified_blocks);
829 u32 diff = modified_blocks.size() - count_was;
830 count_was = modified_blocks.size();
831 infostream<<"spreadLight modified "<<diff<<std::endl;
836 //MapVoxelManipulator vmanip(this);
838 // Make a manual voxel manipulator and load all the blocks
839 // that touch the requested blocks
840 ManualMapVoxelManipulator vmanip(this);
841 core::map<v3s16, MapBlock*>::Iterator i;
842 i = blocks_to_update.getIterator();
843 for(; i.atEnd() == false; i++)
845 MapBlock *block = i.getNode()->getValue();
846 v3s16 p = block->getPos();
848 // Add all surrounding blocks
849 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
852 Add all surrounding blocks that have up-to-date lighting
853 NOTE: This doesn't quite do the job (not everything
854 appropriate is lighted)
856 /*for(s16 z=-1; z<=1; z++)
857 for(s16 y=-1; y<=1; y++)
858 for(s16 x=-1; x<=1; x++)
860 v3s16 p2 = p + v3s16(x,y,z);
861 MapBlock *block = getBlockNoCreateNoEx(p2);
866 if(block->getLightingExpired())
868 vmanip.initialEmerge(p2, p2);
871 // Lighting of block will be updated completely
872 block->setLightingExpired(false);
876 //TimeTaker timer("unSpreadLight");
877 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
880 //TimeTaker timer("spreadLight");
881 vmanip.spreadLight(bank, light_sources, nodemgr);
884 //TimeTaker timer("blitBack");
885 vmanip.blitBack(modified_blocks);
887 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
891 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
894 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
895 core::map<v3s16, MapBlock*> & modified_blocks)
897 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
898 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
901 Update information about whether day and night light differ
903 for(core::map<v3s16, MapBlock*>::Iterator
904 i = modified_blocks.getIterator();
905 i.atEnd() == false; i++)
907 MapBlock *block = i.getNode()->getValue();
908 block->updateDayNightDiff();
914 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
915 core::map<v3s16, MapBlock*> &modified_blocks)
917 INodeDefManager *nodemgr = m_gamedef->ndef();
920 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
921 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
924 From this node to nodes underneath:
925 If lighting is sunlight (1.0), unlight neighbours and
930 v3s16 toppos = p + v3s16(0,1,0);
931 v3s16 bottompos = p + v3s16(0,-1,0);
933 bool node_under_sunlight = true;
934 core::map<v3s16, bool> light_sources;
937 If there is a node at top and it doesn't have sunlight,
938 there has not been any sunlight going down.
940 Otherwise there probably is.
943 MapNode topnode = getNode(toppos);
945 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
946 node_under_sunlight = false;
948 catch(InvalidPositionException &e)
953 Remove all light that has come out of this node
956 enum LightBank banks[] =
961 for(s32 i=0; i<2; i++)
963 enum LightBank bank = banks[i];
965 u8 lightwas = getNode(p).getLight(bank, nodemgr);
967 // Add the block of the added node to modified_blocks
968 v3s16 blockpos = getNodeBlockPos(p);
969 MapBlock * block = getBlockNoCreate(blockpos);
970 assert(block != NULL);
971 modified_blocks.insert(blockpos, block);
973 assert(isValidPosition(p));
975 // Unlight neighbours of node.
976 // This means setting light of all consequent dimmer nodes
978 // This also collects the nodes at the border which will spread
979 // light again into this.
980 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
982 n.setLight(bank, 0, nodemgr);
986 If node lets sunlight through and is under sunlight, it has
989 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
995 Set the node on the map
1004 std::string metadata_name = nodemgr->get(n).metadata_name;
1005 if(metadata_name != ""){
1006 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1008 errorstream<<"Failed to create node metadata \""
1009 <<metadata_name<<"\""<<std::endl;
1011 setNodeMetadata(p, meta);
1016 If node is under sunlight and doesn't let sunlight through,
1017 take all sunlighted nodes under it and clear light from them
1018 and from where the light has been spread.
1019 TODO: This could be optimized by mass-unlighting instead
1022 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1026 //m_dout<<DTIME<<"y="<<y<<std::endl;
1027 v3s16 n2pos(p.X, y, p.Z);
1031 n2 = getNode(n2pos);
1033 catch(InvalidPositionException &e)
1038 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1040 unLightNeighbors(LIGHTBANK_DAY,
1041 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1042 light_sources, modified_blocks);
1043 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1051 for(s32 i=0; i<2; i++)
1053 enum LightBank bank = banks[i];
1056 Spread light from all nodes that might be capable of doing so
1058 spreadLight(bank, light_sources, modified_blocks);
1062 Update information about whether day and night light differ
1064 for(core::map<v3s16, MapBlock*>::Iterator
1065 i = modified_blocks.getIterator();
1066 i.atEnd() == false; i++)
1068 MapBlock *block = i.getNode()->getValue();
1069 block->updateDayNightDiff();
1073 Add neighboring liquid nodes and the node itself if it is
1074 liquid (=water node was added) to transform queue.
1077 v3s16(0,0,0), // self
1078 v3s16(0,0,1), // back
1079 v3s16(0,1,0), // top
1080 v3s16(1,0,0), // right
1081 v3s16(0,0,-1), // front
1082 v3s16(0,-1,0), // bottom
1083 v3s16(-1,0,0), // left
1085 for(u16 i=0; i<7; i++)
1090 v3s16 p2 = p + dirs[i];
1092 MapNode n2 = getNode(p2);
1093 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1095 m_transforming_liquid.push_back(p2);
1098 }catch(InvalidPositionException &e)
1106 void Map::removeNodeAndUpdate(v3s16 p,
1107 core::map<v3s16, MapBlock*> &modified_blocks)
1109 INodeDefManager *nodemgr = m_gamedef->ndef();
1111 /*PrintInfo(m_dout);
1112 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1113 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1115 bool node_under_sunlight = true;
1117 v3s16 toppos = p + v3s16(0,1,0);
1119 // Node will be replaced with this
1120 content_t replace_material = CONTENT_AIR;
1123 If there is a node at top and it doesn't have sunlight,
1124 there will be no sunlight going down.
1127 MapNode topnode = getNode(toppos);
1129 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1130 node_under_sunlight = false;
1132 catch(InvalidPositionException &e)
1136 core::map<v3s16, bool> light_sources;
1138 enum LightBank banks[] =
1143 for(s32 i=0; i<2; i++)
1145 enum LightBank bank = banks[i];
1148 Unlight neighbors (in case the node is a light source)
1150 unLightNeighbors(bank, p,
1151 getNode(p).getLight(bank, nodemgr),
1152 light_sources, modified_blocks);
1156 Remove node metadata
1159 removeNodeMetadata(p);
1163 This also clears the lighting.
1167 n.setContent(replace_material);
1170 for(s32 i=0; i<2; i++)
1172 enum LightBank bank = banks[i];
1175 Recalculate lighting
1177 spreadLight(bank, light_sources, modified_blocks);
1180 // Add the block of the removed node to modified_blocks
1181 v3s16 blockpos = getNodeBlockPos(p);
1182 MapBlock * block = getBlockNoCreate(blockpos);
1183 assert(block != NULL);
1184 modified_blocks.insert(blockpos, block);
1187 If the removed node was under sunlight, propagate the
1188 sunlight down from it and then light all neighbors
1189 of the propagated blocks.
1191 if(node_under_sunlight)
1193 s16 ybottom = propagateSunlight(p, modified_blocks);
1194 /*m_dout<<DTIME<<"Node was under sunlight. "
1195 "Propagating sunlight";
1196 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1198 for(; y >= ybottom; y--)
1200 v3s16 p2(p.X, y, p.Z);
1201 /*m_dout<<DTIME<<"lighting neighbors of node ("
1202 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1204 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1209 // Set the lighting of this node to 0
1210 // TODO: Is this needed? Lighting is cleared up there already.
1212 MapNode n = getNode(p);
1213 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1216 catch(InvalidPositionException &e)
1222 for(s32 i=0; i<2; i++)
1224 enum LightBank bank = banks[i];
1226 // Get the brightest neighbour node and propagate light from it
1227 v3s16 n2p = getBrightestNeighbour(bank, p);
1229 MapNode n2 = getNode(n2p);
1230 lightNeighbors(bank, n2p, modified_blocks);
1232 catch(InvalidPositionException &e)
1238 Update information about whether day and night light differ
1240 for(core::map<v3s16, MapBlock*>::Iterator
1241 i = modified_blocks.getIterator();
1242 i.atEnd() == false; i++)
1244 MapBlock *block = i.getNode()->getValue();
1245 block->updateDayNightDiff();
1249 Add neighboring liquid nodes and this node to transform queue.
1250 (it's vital for the node itself to get updated last.)
1253 v3s16(0,0,1), // back
1254 v3s16(0,1,0), // top
1255 v3s16(1,0,0), // right
1256 v3s16(0,0,-1), // front
1257 v3s16(0,-1,0), // bottom
1258 v3s16(-1,0,0), // left
1259 v3s16(0,0,0), // self
1261 for(u16 i=0; i<7; i++)
1266 v3s16 p2 = p + dirs[i];
1268 MapNode n2 = getNode(p2);
1269 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1271 m_transforming_liquid.push_back(p2);
1274 }catch(InvalidPositionException &e)
1280 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1283 event.type = MEET_ADDNODE;
1287 bool succeeded = true;
1289 core::map<v3s16, MapBlock*> modified_blocks;
1290 addNodeAndUpdate(p, n, modified_blocks);
1292 // Copy modified_blocks to event
1293 for(core::map<v3s16, MapBlock*>::Iterator
1294 i = modified_blocks.getIterator();
1295 i.atEnd()==false; i++)
1297 event.modified_blocks.insert(i.getNode()->getKey(), false);
1300 catch(InvalidPositionException &e){
1304 dispatchEvent(&event);
1309 bool Map::removeNodeWithEvent(v3s16 p)
1312 event.type = MEET_REMOVENODE;
1315 bool succeeded = true;
1317 core::map<v3s16, MapBlock*> modified_blocks;
1318 removeNodeAndUpdate(p, modified_blocks);
1320 // Copy modified_blocks to event
1321 for(core::map<v3s16, MapBlock*>::Iterator
1322 i = modified_blocks.getIterator();
1323 i.atEnd()==false; i++)
1325 event.modified_blocks.insert(i.getNode()->getKey(), false);
1328 catch(InvalidPositionException &e){
1332 dispatchEvent(&event);
1337 bool Map::dayNightDiffed(v3s16 blockpos)
1340 v3s16 p = blockpos + v3s16(0,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1348 v3s16 p = blockpos + v3s16(-1,0,0);
1349 MapBlock *b = getBlockNoCreate(p);
1350 if(b->dayNightDiffed())
1353 catch(InvalidPositionException &e){}
1355 v3s16 p = blockpos + v3s16(0,-1,0);
1356 MapBlock *b = getBlockNoCreate(p);
1357 if(b->dayNightDiffed())
1360 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(0,0,-1);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1370 v3s16 p = blockpos + v3s16(1,0,0);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->dayNightDiffed())
1375 catch(InvalidPositionException &e){}
1377 v3s16 p = blockpos + v3s16(0,1,0);
1378 MapBlock *b = getBlockNoCreate(p);
1379 if(b->dayNightDiffed())
1382 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(0,0,1);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->dayNightDiffed())
1389 catch(InvalidPositionException &e){}
1395 Updates usage timers
1397 void Map::timerUpdate(float dtime, float unload_timeout,
1398 core::list<v3s16> *unloaded_blocks)
1400 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1402 // Profile modified reasons
1403 Profiler modprofiler;
1405 core::list<v2s16> sector_deletion_queue;
1406 u32 deleted_blocks_count = 0;
1407 u32 saved_blocks_count = 0;
1408 u32 block_count_all = 0;
1410 core::map<v2s16, MapSector*>::Iterator si;
1413 si = m_sectors.getIterator();
1414 for(; si.atEnd() == false; si++)
1416 MapSector *sector = si.getNode()->getValue();
1418 bool all_blocks_deleted = true;
1420 core::list<MapBlock*> blocks;
1421 sector->getBlocks(blocks);
1423 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1424 i != blocks.end(); i++)
1426 MapBlock *block = (*i);
1428 block->incrementUsageTimer(dtime);
1430 if(block->getUsageTimer() > unload_timeout)
1432 v3s16 p = block->getPos();
1435 if(block->getModified() != MOD_STATE_CLEAN
1436 && save_before_unloading)
1438 modprofiler.add(block->getModifiedReason(), 1);
1440 saved_blocks_count++;
1443 // Delete from memory
1444 sector->deleteBlock(block);
1447 unloaded_blocks->push_back(p);
1449 deleted_blocks_count++;
1453 all_blocks_deleted = false;
1458 if(all_blocks_deleted)
1460 sector_deletion_queue.push_back(si.getNode()->getKey());
1465 // Finally delete the empty sectors
1466 deleteSectors(sector_deletion_queue);
1468 if(deleted_blocks_count != 0)
1470 PrintInfo(infostream); // ServerMap/ClientMap:
1471 infostream<<"Unloaded "<<deleted_blocks_count
1472 <<" blocks from memory";
1473 if(save_before_unloading)
1474 infostream<<", of which "<<saved_blocks_count<<" were written";
1475 infostream<<", "<<block_count_all<<" blocks in memory";
1476 infostream<<"."<<std::endl;
1477 if(saved_blocks_count != 0){
1478 PrintInfo(infostream); // ServerMap/ClientMap:
1479 infostream<<"Blocks modified by: "<<std::endl;
1480 modprofiler.print(infostream);
1485 void Map::deleteSectors(core::list<v2s16> &list)
1487 core::list<v2s16>::Iterator j;
1488 for(j=list.begin(); j!=list.end(); j++)
1490 MapSector *sector = m_sectors[*j];
1491 // If sector is in sector cache, remove it from there
1492 if(m_sector_cache == sector)
1493 m_sector_cache = NULL;
1494 // Remove from map and delete
1495 m_sectors.remove(*j);
1501 void Map::unloadUnusedData(float timeout,
1502 core::list<v3s16> *deleted_blocks)
1504 core::list<v2s16> sector_deletion_queue;
1505 u32 deleted_blocks_count = 0;
1506 u32 saved_blocks_count = 0;
1508 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1509 for(; si.atEnd() == false; si++)
1511 MapSector *sector = si.getNode()->getValue();
1513 bool all_blocks_deleted = true;
1515 core::list<MapBlock*> blocks;
1516 sector->getBlocks(blocks);
1517 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1518 i != blocks.end(); i++)
1520 MapBlock *block = (*i);
1522 if(block->getUsageTimer() > timeout)
1525 if(block->getModified() != MOD_STATE_CLEAN)
1528 saved_blocks_count++;
1530 // Delete from memory
1531 sector->deleteBlock(block);
1532 deleted_blocks_count++;
1536 all_blocks_deleted = false;
1540 if(all_blocks_deleted)
1542 sector_deletion_queue.push_back(si.getNode()->getKey());
1546 deleteSectors(sector_deletion_queue);
1548 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1549 <<", of which "<<saved_blocks_count<<" were wr."
1552 //return sector_deletion_queue.getSize();
1553 //return deleted_blocks_count;
1557 void Map::PrintInfo(std::ostream &out)
1562 #define WATER_DROP_BOOST 4
1566 NEIGHBOR_SAME_LEVEL,
1569 struct NodeNeighbor {
1575 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1577 INodeDefManager *nodemgr = m_gamedef->ndef();
1579 DSTACK(__FUNCTION_NAME);
1580 //TimeTaker timer("transformLiquids()");
1583 u32 initial_size = m_transforming_liquid.size();
1585 /*if(initial_size != 0)
1586 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1588 // list of nodes that due to viscosity have not reached their max level height
1589 UniqueQueue<v3s16> must_reflow;
1591 // List of MapBlocks that will require a lighting update (due to lava)
1592 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1594 while(m_transforming_liquid.size() != 0)
1596 // This should be done here so that it is done when continue is used
1597 if(loopcount >= initial_size * 3)
1602 Get a queued transforming liquid node
1604 v3s16 p0 = m_transforming_liquid.pop_front();
1606 MapNode n0 = getNodeNoEx(p0);
1609 Collect information about current node
1611 s8 liquid_level = -1;
1612 u8 liquid_kind = CONTENT_IGNORE;
1613 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1614 switch (liquid_type) {
1616 liquid_level = LIQUID_LEVEL_SOURCE;
1617 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1619 case LIQUID_FLOWING:
1620 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1621 liquid_kind = n0.getContent();
1624 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1625 // continue with the next node.
1626 if (n0.getContent() != CONTENT_AIR)
1628 liquid_kind = CONTENT_AIR;
1633 Collect information about the environment
1635 const v3s16 *dirs = g_6dirs;
1636 NodeNeighbor sources[6]; // surrounding sources
1637 int num_sources = 0;
1638 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1640 NodeNeighbor airs[6]; // surrounding air
1642 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1643 int num_neutrals = 0;
1644 bool flowing_down = false;
1645 for (u16 i = 0; i < 6; i++) {
1646 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1649 nt = NEIGHBOR_UPPER;
1652 nt = NEIGHBOR_LOWER;
1655 v3s16 npos = p0 + dirs[i];
1656 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1657 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1659 if (nb.n.getContent() == CONTENT_AIR) {
1660 airs[num_airs++] = nb;
1661 // if the current node is a water source the neighbor
1662 // should be enqueded for transformation regardless of whether the
1663 // current node changes or not.
1664 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1665 m_transforming_liquid.push_back(npos);
1666 // if the current node happens to be a flowing node, it will start to flow down here.
1667 if (nb.t == NEIGHBOR_LOWER) {
1668 flowing_down = true;
1671 neutrals[num_neutrals++] = nb;
1675 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 if (liquid_kind == CONTENT_AIR)
1677 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1678 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1679 neutrals[num_neutrals++] = nb;
1681 // Do not count bottom source, it will screw things up
1683 sources[num_sources++] = nb;
1686 case LIQUID_FLOWING:
1687 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1688 if (liquid_kind == CONTENT_AIR)
1689 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1690 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1691 neutrals[num_neutrals++] = nb;
1693 flows[num_flows++] = nb;
1694 if (nb.t == NEIGHBOR_LOWER)
1695 flowing_down = true;
1702 decide on the type (and possibly level) of the current node
1704 content_t new_node_content;
1705 s8 new_node_level = -1;
1706 s8 max_node_level = -1;
1707 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1708 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1709 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1710 // it's perfectly safe to use liquid_kind here to determine the new node content.
1711 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1712 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1713 // liquid_kind is set properly, see above
1714 new_node_content = liquid_kind;
1715 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1717 // no surrounding sources, so get the maximum level that can flow into this node
1718 for (u16 i = 0; i < num_flows; i++) {
1719 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1720 switch (flows[i].t) {
1721 case NEIGHBOR_UPPER:
1722 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1723 max_node_level = LIQUID_LEVEL_MAX;
1724 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1725 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1726 } else if (nb_liquid_level > max_node_level)
1727 max_node_level = nb_liquid_level;
1729 case NEIGHBOR_LOWER:
1731 case NEIGHBOR_SAME_LEVEL:
1732 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1733 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1734 max_node_level = nb_liquid_level - 1;
1740 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1741 if (viscosity > 1 && max_node_level != liquid_level) {
1742 // amount to gain, limited by viscosity
1743 // must be at least 1 in absolute value
1744 s8 level_inc = max_node_level - liquid_level;
1745 if (level_inc < -viscosity || level_inc > viscosity)
1746 new_node_level = liquid_level + level_inc/viscosity;
1747 else if (level_inc < 0)
1748 new_node_level = liquid_level - 1;
1749 else if (level_inc > 0)
1750 new_node_level = liquid_level + 1;
1751 if (new_node_level != max_node_level)
1752 must_reflow.push_back(p0);
1754 new_node_level = max_node_level;
1756 if (new_node_level >= 0)
1757 new_node_content = liquid_kind;
1759 new_node_content = CONTENT_AIR;
1764 check if anything has changed. if not, just continue with the next node.
1766 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1767 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1768 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1774 update the current node
1776 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1777 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1778 // set level to last 3 bits, flowing down bit to 4th bit
1779 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1781 // set the liquid level and flow bit to 0
1782 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1784 n0.setContent(new_node_content);
1786 v3s16 blockpos = getNodeBlockPos(p0);
1787 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1789 modified_blocks.insert(blockpos, block);
1790 // If node emits light, MapBlock requires lighting update
1791 if(nodemgr->get(n0).light_source != 0)
1792 lighting_modified_blocks[block->getPos()] = block;
1796 enqueue neighbors for update if neccessary
1798 switch (nodemgr->get(n0.getContent()).liquid_type) {
1800 case LIQUID_FLOWING:
1801 // make sure source flows into all neighboring nodes
1802 for (u16 i = 0; i < num_flows; i++)
1803 if (flows[i].t != NEIGHBOR_UPPER)
1804 m_transforming_liquid.push_back(flows[i].p);
1805 for (u16 i = 0; i < num_airs; i++)
1806 if (airs[i].t != NEIGHBOR_UPPER)
1807 m_transforming_liquid.push_back(airs[i].p);
1810 // this flow has turned to air; neighboring flows might need to do the same
1811 for (u16 i = 0; i < num_flows; i++)
1812 m_transforming_liquid.push_back(flows[i].p);
1816 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1817 while (must_reflow.size() > 0)
1818 m_transforming_liquid.push_back(must_reflow.pop_front());
1819 updateLighting(lighting_modified_blocks, modified_blocks);
1822 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1824 v3s16 blockpos = getNodeBlockPos(p);
1825 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1826 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1828 infostream<<"Map::getNodeMetadata(): Need to emerge "
1829 <<PP(blockpos)<<std::endl;
1830 block = emergeBlock(blockpos, false);
1834 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1838 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1842 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1844 v3s16 blockpos = getNodeBlockPos(p);
1845 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1846 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1848 infostream<<"Map::setNodeMetadata(): Need to emerge "
1849 <<PP(blockpos)<<std::endl;
1850 block = emergeBlock(blockpos, false);
1854 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1858 block->m_node_metadata->set(p_rel, meta);
1861 void Map::removeNodeMetadata(v3s16 p)
1863 v3s16 blockpos = getNodeBlockPos(p);
1864 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1865 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1868 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1872 block->m_node_metadata->remove(p_rel);
1875 void Map::nodeMetadataStep(float dtime,
1876 core::map<v3s16, MapBlock*> &changed_blocks)
1880 Currently there is no way to ensure that all the necessary
1881 blocks are loaded when this is run. (They might get unloaded)
1882 NOTE: ^- Actually, that might not be so. In a quick test it
1883 reloaded a block with a furnace when I walked back to it from
1886 core::map<v2s16, MapSector*>::Iterator si;
1887 si = m_sectors.getIterator();
1888 for(; si.atEnd() == false; si++)
1890 MapSector *sector = si.getNode()->getValue();
1891 core::list< MapBlock * > sectorblocks;
1892 sector->getBlocks(sectorblocks);
1893 core::list< MapBlock * >::Iterator i;
1894 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1896 MapBlock *block = *i;
1897 bool changed = block->m_node_metadata->step(dtime);
1899 changed_blocks[block->getPos()] = block;
1908 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1909 Map(dout_server, gamedef),
1911 m_map_metadata_changed(true),
1913 m_database_read(NULL),
1914 m_database_write(NULL)
1916 verbosestream<<__FUNCTION_NAME<<std::endl;
1918 //m_chunksize = 8; // Takes a few seconds
1920 if (g_settings->get("fixed_map_seed").empty())
1922 m_seed = (((u64)(myrand()%0xffff)<<0)
1923 + ((u64)(myrand()%0xffff)<<16)
1924 + ((u64)(myrand()%0xffff)<<32)
1925 + ((u64)(myrand()%0xffff)<<48));
1929 m_seed = g_settings->getU64("fixed_map_seed");
1933 Experimental and debug stuff
1940 Try to load map; if not found, create a new one.
1943 m_savedir = savedir;
1944 m_map_saving_enabled = false;
1948 // If directory exists, check contents and load if possible
1949 if(fs::PathExists(m_savedir))
1951 // If directory is empty, it is safe to save into it.
1952 if(fs::GetDirListing(m_savedir).size() == 0)
1954 infostream<<"ServerMap: Empty save directory is valid."
1956 m_map_saving_enabled = true;
1961 // Load map metadata (seed, chunksize)
1964 catch(FileNotGoodException &e){
1965 infostream<<"WARNING: Could not load map metadata"
1966 //<<" Disabling chunk-based generator."
1971 infostream<<"ServerMap: Successfully loaded map "
1972 <<"metadata from "<<savedir
1973 <<", assuming valid save directory."
1974 <<" seed="<<m_seed<<"."
1977 m_map_saving_enabled = true;
1978 // Map loaded, not creating new one
1982 // If directory doesn't exist, it is safe to save to it
1984 m_map_saving_enabled = true;
1987 catch(std::exception &e)
1989 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
1990 <<", exception: "<<e.what()<<std::endl;
1991 infostream<<"Please remove the map or fix it."<<std::endl;
1992 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1995 infostream<<"Initializing new map."<<std::endl;
1997 // Create zero sector
1998 emergeSector(v2s16(0,0));
2000 // Initially write whole map
2001 save(MOD_STATE_CLEAN);
2004 ServerMap::~ServerMap()
2006 verbosestream<<__FUNCTION_NAME<<std::endl;
2010 if(m_map_saving_enabled)
2012 // Save only changed parts
2013 save(MOD_STATE_WRITE_AT_UNLOAD);
2014 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2018 infostream<<"ServerMap: Map not saved"<<std::endl;
2021 catch(std::exception &e)
2023 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2024 <<", exception: "<<e.what()<<std::endl;
2028 Close database if it was opened
2031 sqlite3_finalize(m_database_read);
2032 if(m_database_write)
2033 sqlite3_finalize(m_database_write);
2035 sqlite3_close(m_database);
2041 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2042 for(; i.atEnd() == false; i++)
2044 MapChunk *chunk = i.getNode()->getValue();
2050 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2052 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2053 if(enable_mapgen_debug_info)
2054 infostream<<"initBlockMake(): "
2055 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2056 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2060 v3s16 blockpos_div = getContainerPos(blockpos, chunksize);
2061 v3s16 blockpos_min = blockpos_div * chunksize;
2062 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2064 // Do nothing if not inside limits (+-1 because of neighbors)
2065 if(blockpos_over_limit(blockpos_min - v3s16(1,1,1)) ||
2066 blockpos_over_limit(blockpos_max + v3s16(1,1,1)))
2072 data->no_op = false;
2073 data->seed = m_seed;
2074 data->blockpos_min = blockpos_min;
2075 data->blockpos_max = blockpos_max;
2076 data->blockpos_requested = blockpos;
2077 data->nodedef = m_gamedef->ndef();
2080 Create the whole area of this and the neighboring blocks
2083 //TimeTaker timer("initBlockMake() create area");
2085 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2086 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2088 v2s16 sectorpos(x, z);
2089 // Sector metadata is loaded from disk if not already loaded.
2090 ServerMapSector *sector = createSector(sectorpos);
2093 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2096 //MapBlock *block = createBlock(p);
2097 // 1) get from memory, 2) load from disk
2098 MapBlock *block = emergeBlock(p, false);
2099 // 3) create a blank one
2102 block = createBlock(p);
2105 Block gets sunlight if this is true.
2107 Refer to the map generator heuristics.
2109 bool ug = mapgen::block_is_underground(data->seed, p);
2110 block->setIsUnderground(ug);
2113 // Lighting will not be valid after make_chunk is called
2114 block->setLightingExpired(true);
2115 // Lighting will be calculated
2116 //block->setLightingExpired(false);
2122 Now we have a big empty area.
2124 Make a ManualMapVoxelManipulator that contains this and the
2128 // The area that contains this block and it's neighbors
2129 v3s16 bigarea_blocks_min = blockpos_min - v3s16(1,1,1);
2130 v3s16 bigarea_blocks_max = blockpos_max + v3s16(1,1,1);
2132 data->vmanip = new ManualMapVoxelManipulator(this);
2133 //data->vmanip->setMap(this);
2137 //TimeTaker timer("initBlockMake() initialEmerge");
2138 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2141 // Data is ready now.
2144 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2145 core::map<v3s16, MapBlock*> &changed_blocks)
2147 v3s16 blockpos_min = data->blockpos_min;
2148 v3s16 blockpos_max = data->blockpos_max;
2149 v3s16 blockpos_requested = data->blockpos_requested;
2150 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2151 <<blockpos_requested.Y<<","
2152 <<blockpos_requested.Z<<")"<<std::endl;*/
2156 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2160 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2162 /*infostream<<"Resulting vmanip:"<<std::endl;
2163 data->vmanip.print(infostream);*/
2165 // Make sure affected blocks are loaded
2166 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2167 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2168 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2171 // Load from disk if not already in memory
2172 emergeBlock(p, false);
2176 Blit generated stuff to map
2177 NOTE: blitBackAll adds nearly everything to changed_blocks
2181 //TimeTaker timer("finishBlockMake() blitBackAll");
2182 data->vmanip->blitBackAll(&changed_blocks);
2185 if(enable_mapgen_debug_info)
2186 infostream<<"finishBlockMake: changed_blocks.size()="
2187 <<changed_blocks.size()<<std::endl;
2190 Copy transforming liquid information
2192 while(data->transforming_liquid.size() > 0)
2194 v3s16 p = data->transforming_liquid.pop_front();
2195 m_transforming_liquid.push_back(p);
2199 Do stuff in central blocks
2206 TimeTaker t("finishBlockMake lighting update");
2208 core::map<v3s16, MapBlock*> lighting_update_blocks;
2211 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2212 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2213 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2216 MapBlock *block = getBlockNoCreateNoEx(p);
2218 lighting_update_blocks.insert(block->getPos(), block);
2221 updateLighting(lighting_update_blocks, changed_blocks);
2224 Set lighting to non-expired state in all of them.
2225 This is cheating, but it is not fast enough if all of them
2226 would actually be updated.
2228 for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2229 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2230 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)
2233 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2236 if(enable_mapgen_debug_info == false)
2237 t.stop(true); // Hide output
2241 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2242 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2243 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2246 MapBlock *block = getBlockNoCreateNoEx(p);
2250 Add random objects to block
2252 mapgen::add_random_objects(block);
2256 Go through changed blocks
2258 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2259 i.atEnd() == false; i++)
2261 MapBlock *block = i.getNode()->getValue();
2264 Update day/night difference cache of the MapBlocks
2266 block->updateDayNightDiff();
2268 Set block as modified
2270 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2271 "finishBlockMake updateDayNightDiff");
2275 Set central blocks as generated
2277 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2278 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2279 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2282 MapBlock *block = getBlockNoCreateNoEx(p);
2284 block->setGenerated(true);
2288 Save changed parts of map
2289 NOTE: Will be saved later.
2291 //save(MOD_STATE_WRITE_AT_UNLOAD);
2293 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2294 <<","<<blockpos_requested.Y<<","
2295 <<blockpos_requested.Z<<")"<<std::endl;*/
2297 if(enable_mapgen_debug_info)
2300 Analyze resulting blocks
2302 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2303 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2304 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2305 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2306 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2307 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2309 v3s16 p = v3s16(x,y,z);
2310 MapBlock *block = getBlockNoCreateNoEx(p);
2312 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2313 infostream<<"Generated "<<spos<<": "
2314 <<analyze_block(block)<<std::endl;
2319 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2325 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2327 DSTACKF("%s: p2d=(%d,%d)",
2332 Check if it exists already in memory
2334 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2339 Try to load it from disk (with blocks)
2341 //if(loadSectorFull(p2d) == true)
2344 Try to load metadata from disk
2347 if(loadSectorMeta(p2d) == true)
2349 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2352 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2353 throw InvalidPositionException("");
2359 Do not create over-limit
2361 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2362 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2363 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2364 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2365 throw InvalidPositionException("createSector(): pos. over limit");
2368 Generate blank sector
2371 sector = new ServerMapSector(this, p2d, m_gamedef);
2373 // Sector position on map in nodes
2374 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2379 m_sectors.insert(p2d, sector);
2385 This is a quick-hand function for calling makeBlock().
2387 MapBlock * ServerMap::generateBlock(
2389 core::map<v3s16, MapBlock*> &modified_blocks
2392 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2394 /*infostream<<"generateBlock(): "
2395 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2398 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2400 TimeTaker timer("generateBlock");
2402 //MapBlock *block = original_dummy;
2404 v2s16 p2d(p.X, p.Z);
2405 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2408 Do not generate over-limit
2410 if(blockpos_over_limit(p))
2412 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2413 throw InvalidPositionException("generateBlock(): pos. over limit");
2417 Create block make data
2419 mapgen::BlockMakeData data;
2420 initBlockMake(&data, p);
2426 TimeTaker t("mapgen::make_block()");
2427 mapgen::make_block(&data);
2429 if(enable_mapgen_debug_info == false)
2430 t.stop(true); // Hide output
2434 Blit data back on map, update lighting, add mobs and whatever this does
2436 finishBlockMake(&data, modified_blocks);
2441 MapBlock *block = getBlockNoCreateNoEx(p);
2449 bool erroneus_content = false;
2450 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2451 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2452 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2455 MapNode n = block->getNode(p);
2456 if(n.getContent() == CONTENT_IGNORE)
2458 infostream<<"CONTENT_IGNORE at "
2459 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2461 erroneus_content = true;
2465 if(erroneus_content)
2474 Generate a completely empty block
2478 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2479 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2481 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2484 n.setContent(CONTENT_AIR);
2485 block->setNode(v3s16(x0,y0,z0), n);
2491 if(enable_mapgen_debug_info == false)
2492 timer.stop(true); // Hide output
2497 MapBlock * ServerMap::createBlock(v3s16 p)
2499 DSTACKF("%s: p=(%d,%d,%d)",
2500 __FUNCTION_NAME, p.X, p.Y, p.Z);
2503 Do not create over-limit
2505 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2506 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2507 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2509 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2510 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2511 throw InvalidPositionException("createBlock(): pos. over limit");
2513 v2s16 p2d(p.X, p.Z);
2516 This will create or load a sector if not found in memory.
2517 If block exists on disk, it will be loaded.
2519 NOTE: On old save formats, this will be slow, as it generates
2520 lighting on blocks for them.
2522 ServerMapSector *sector;
2524 sector = (ServerMapSector*)createSector(p2d);
2525 assert(sector->getId() == MAPSECTOR_SERVER);
2527 catch(InvalidPositionException &e)
2529 infostream<<"createBlock: createSector() failed"<<std::endl;
2533 NOTE: This should not be done, or at least the exception
2534 should not be passed on as std::exception, because it
2535 won't be catched at all.
2537 /*catch(std::exception &e)
2539 infostream<<"createBlock: createSector() failed: "
2540 <<e.what()<<std::endl;
2545 Try to get a block from the sector
2548 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2551 if(block->isDummy())
2556 block = sector->createBlankBlock(block_y);
2560 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2562 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2564 p.X, p.Y, p.Z, allow_generate);
2567 MapBlock *block = getBlockNoCreateNoEx(p);
2568 if(block && block->isDummy() == false)
2573 MapBlock *block = loadBlock(p);
2580 core::map<v3s16, MapBlock*> modified_blocks;
2581 MapBlock *block = generateBlock(p, modified_blocks);
2585 event.type = MEET_OTHER;
2588 // Copy modified_blocks to event
2589 for(core::map<v3s16, MapBlock*>::Iterator
2590 i = modified_blocks.getIterator();
2591 i.atEnd()==false; i++)
2593 event.modified_blocks.insert(i.getNode()->getKey(), false);
2597 dispatchEvent(&event);
2606 s16 ServerMap::findGroundLevel(v2s16 p2d)
2610 Uh, just do something random...
2612 // Find existing map from top to down
2615 v3s16 p(p2d.X, max, p2d.Y);
2616 for(; p.Y>min; p.Y--)
2618 MapNode n = getNodeNoEx(p);
2619 if(n.getContent() != CONTENT_IGNORE)
2624 // If this node is not air, go to plan b
2625 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2627 // Search existing walkable and return it
2628 for(; p.Y>min; p.Y--)
2630 MapNode n = getNodeNoEx(p);
2631 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2640 Determine from map generator noise functions
2643 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2646 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2647 //return (s16)level;
2650 void ServerMap::createDatabase() {
2653 e = sqlite3_exec(m_database,
2654 "CREATE TABLE IF NOT EXISTS `blocks` ("
2655 "`pos` INT NOT NULL PRIMARY KEY,"
2658 , NULL, NULL, NULL);
2659 if(e == SQLITE_ABORT)
2660 throw FileNotGoodException("Could not create database structure");
2662 infostream<<"ServerMap: Database structure was created";
2665 void ServerMap::verifyDatabase() {
2670 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2671 bool needs_create = false;
2675 Open the database connection
2678 createDirs(m_savedir);
2680 if(!fs::PathExists(dbp))
2681 needs_create = true;
2683 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2684 if(d != SQLITE_OK) {
2685 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2686 throw FileNotGoodException("Cannot open database file");
2692 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2693 if(d != SQLITE_OK) {
2694 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2695 throw FileNotGoodException("Cannot prepare read statement");
2698 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2699 if(d != SQLITE_OK) {
2700 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2701 throw FileNotGoodException("Cannot prepare write statement");
2704 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2705 if(d != SQLITE_OK) {
2706 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2707 throw FileNotGoodException("Cannot prepare read statement");
2710 infostream<<"ServerMap: Database opened"<<std::endl;
2714 bool ServerMap::loadFromFolders() {
2715 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2720 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2721 return (sqlite3_int64)pos.Z*16777216 +
2722 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2725 void ServerMap::createDirs(std::string path)
2727 if(fs::CreateAllDirs(path) == false)
2729 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2730 <<"\""<<path<<"\""<<std::endl;
2731 throw BaseException("ServerMap failed to create directory");
2735 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2741 snprintf(cc, 9, "%.4x%.4x",
2742 (unsigned int)pos.X&0xffff,
2743 (unsigned int)pos.Y&0xffff);
2745 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2747 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2748 (unsigned int)pos.X&0xfff,
2749 (unsigned int)pos.Y&0xfff);
2751 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2757 v2s16 ServerMap::getSectorPos(std::string dirname)
2761 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2762 assert(spos != std::string::npos);
2763 if(dirname.size() - spos == 8)
2766 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2768 else if(dirname.size() - spos == 3)
2771 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2772 // Sign-extend the 12 bit values up to 16 bits...
2773 if(x&0x800) x|=0xF000;
2774 if(y&0x800) y|=0xF000;
2781 v2s16 pos((s16)x, (s16)y);
2785 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2787 v2s16 p2d = getSectorPos(sectordir);
2789 if(blockfile.size() != 4){
2790 throw InvalidFilenameException("Invalid block filename");
2793 int r = sscanf(blockfile.c_str(), "%4x", &y);
2795 throw InvalidFilenameException("Invalid block filename");
2796 return v3s16(p2d.X, y, p2d.Y);
2799 std::string ServerMap::getBlockFilename(v3s16 p)
2802 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2806 void ServerMap::save(ModifiedState save_level)
2808 DSTACK(__FUNCTION_NAME);
2809 if(m_map_saving_enabled == false)
2811 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2815 if(save_level == MOD_STATE_CLEAN)
2816 infostream<<"ServerMap: Saving whole map, this can take time."
2819 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2824 // Profile modified reasons
2825 Profiler modprofiler;
2827 u32 sector_meta_count = 0;
2828 u32 block_count = 0;
2829 u32 block_count_all = 0; // Number of blocks in memory
2831 // Don't do anything with sqlite unless something is really saved
2832 bool save_started = false;
2834 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2835 for(; i.atEnd() == false; i++)
2837 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2838 assert(sector->getId() == MAPSECTOR_SERVER);
2840 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2842 saveSectorMeta(sector);
2843 sector_meta_count++;
2845 core::list<MapBlock*> blocks;
2846 sector->getBlocks(blocks);
2847 core::list<MapBlock*>::Iterator j;
2849 for(j=blocks.begin(); j!=blocks.end(); j++)
2851 MapBlock *block = *j;
2855 if(block->getModified() >= save_level)
2860 save_started = true;
2863 modprofiler.add(block->getModifiedReason(), 1);
2868 /*infostream<<"ServerMap: Written block ("
2869 <<block->getPos().X<<","
2870 <<block->getPos().Y<<","
2871 <<block->getPos().Z<<")"
2880 Only print if something happened or saved whole map
2882 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2883 || block_count != 0)
2885 infostream<<"ServerMap: Written: "
2886 <<sector_meta_count<<" sector metadata files, "
2887 <<block_count<<" block files"
2888 <<", "<<block_count_all<<" blocks in memory."
2890 PrintInfo(infostream); // ServerMap/ClientMap:
2891 infostream<<"Blocks modified by: "<<std::endl;
2892 modprofiler.print(infostream);
2896 static s32 unsignedToSigned(s32 i, s32 max_positive)
2898 if(i < max_positive)
2901 return i - 2*max_positive;
2904 // modulo of a negative number does not work consistently in C
2905 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2909 return mod - ((-i) % mod);
2912 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2914 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2916 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2918 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2919 return v3s16(x,y,z);
2922 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2924 if(loadFromFolders()){
2925 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2926 <<"all blocks that are stored in flat files"<<std::endl;
2932 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2934 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2935 v3s16 p = getIntegerAsBlock(block_i);
2936 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2942 void ServerMap::saveMapMeta()
2944 DSTACK(__FUNCTION_NAME);
2946 /*infostream<<"ServerMap::saveMapMeta(): "
2950 createDirs(m_savedir);
2952 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2953 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2954 if(os.good() == false)
2956 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2957 <<"could not open"<<fullpath<<std::endl;
2958 throw FileNotGoodException("Cannot open chunk metadata");
2962 params.setU64("seed", m_seed);
2964 params.writeLines(os);
2966 os<<"[end_of_params]\n";
2968 m_map_metadata_changed = false;
2971 void ServerMap::loadMapMeta()
2973 DSTACK(__FUNCTION_NAME);
2975 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2978 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2979 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2980 if(is.good() == false)
2982 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2983 <<"could not open"<<fullpath<<std::endl;
2984 throw FileNotGoodException("Cannot open map metadata");
2992 throw SerializationError
2993 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2995 std::getline(is, line);
2996 std::string trimmedline = trim(line);
2997 if(trimmedline == "[end_of_params]")
2999 params.parseConfigLine(line);
3002 m_seed = params.getU64("seed");
3004 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3007 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3009 DSTACK(__FUNCTION_NAME);
3010 // Format used for writing
3011 u8 version = SER_FMT_VER_HIGHEST;
3013 v2s16 pos = sector->getPos();
3014 std::string dir = getSectorDir(pos);
3017 std::string fullpath = dir + DIR_DELIM + "meta";
3018 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3019 if(o.good() == false)
3020 throw FileNotGoodException("Cannot open sector metafile");
3022 sector->serialize(o, version);
3024 sector->differs_from_disk = false;
3027 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3029 DSTACK(__FUNCTION_NAME);
3031 v2s16 p2d = getSectorPos(sectordir);
3033 ServerMapSector *sector = NULL;
3035 std::string fullpath = sectordir + DIR_DELIM + "meta";
3036 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3037 if(is.good() == false)
3039 // If the directory exists anyway, it probably is in some old
3040 // format. Just go ahead and create the sector.
3041 if(fs::PathExists(sectordir))
3043 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3044 <<fullpath<<" doesn't exist but directory does."
3045 <<" Continuing with a sector with no metadata."
3047 sector = new ServerMapSector(this, p2d, m_gamedef);
3048 m_sectors.insert(p2d, sector);
3052 throw FileNotGoodException("Cannot open sector metafile");
3057 sector = ServerMapSector::deSerialize
3058 (is, this, p2d, m_sectors, m_gamedef);
3060 saveSectorMeta(sector);
3063 sector->differs_from_disk = false;
3068 bool ServerMap::loadSectorMeta(v2s16 p2d)
3070 DSTACK(__FUNCTION_NAME);
3072 MapSector *sector = NULL;
3074 // The directory layout we're going to load from.
3075 // 1 - original sectors/xxxxzzzz/
3076 // 2 - new sectors2/xxx/zzz/
3077 // If we load from anything but the latest structure, we will
3078 // immediately save to the new one, and remove the old.
3080 std::string sectordir1 = getSectorDir(p2d, 1);
3081 std::string sectordir;
3082 if(fs::PathExists(sectordir1))
3084 sectordir = sectordir1;
3089 sectordir = getSectorDir(p2d, 2);
3093 sector = loadSectorMeta(sectordir, loadlayout != 2);
3095 catch(InvalidFilenameException &e)
3099 catch(FileNotGoodException &e)
3103 catch(std::exception &e)
3112 bool ServerMap::loadSectorFull(v2s16 p2d)
3114 DSTACK(__FUNCTION_NAME);
3116 MapSector *sector = NULL;
3118 // The directory layout we're going to load from.
3119 // 1 - original sectors/xxxxzzzz/
3120 // 2 - new sectors2/xxx/zzz/
3121 // If we load from anything but the latest structure, we will
3122 // immediately save to the new one, and remove the old.
3124 std::string sectordir1 = getSectorDir(p2d, 1);
3125 std::string sectordir;
3126 if(fs::PathExists(sectordir1))
3128 sectordir = sectordir1;
3133 sectordir = getSectorDir(p2d, 2);
3137 sector = loadSectorMeta(sectordir, loadlayout != 2);
3139 catch(InvalidFilenameException &e)
3143 catch(FileNotGoodException &e)
3147 catch(std::exception &e)
3155 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3157 std::vector<fs::DirListNode>::iterator i2;
3158 for(i2=list2.begin(); i2!=list2.end(); i2++)
3164 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3166 catch(InvalidFilenameException &e)
3168 // This catches unknown crap in directory
3174 infostream<<"Sector converted to new layout - deleting "<<
3175 sectordir1<<std::endl;
3176 fs::RecursiveDelete(sectordir1);
3183 void ServerMap::beginSave() {
3185 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3186 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3189 void ServerMap::endSave() {
3191 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3192 infostream<<"WARNING: endSave() failed, map might not have saved.";
3195 void ServerMap::saveBlock(MapBlock *block)
3197 DSTACK(__FUNCTION_NAME);
3199 Dummy blocks are not written
3201 if(block->isDummy())
3203 /*v3s16 p = block->getPos();
3204 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3205 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3209 // Format used for writing
3210 u8 version = SER_FMT_VER_HIGHEST;
3212 v3s16 p3d = block->getPos();
3216 v2s16 p2d(p3d.X, p3d.Z);
3217 std::string sectordir = getSectorDir(p2d);
3219 createDirs(sectordir);
3221 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3222 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3223 if(o.good() == false)
3224 throw FileNotGoodException("Cannot open block data");
3227 [0] u8 serialization version
3233 std::ostringstream o(std::ios_base::binary);
3235 o.write((char*)&version, 1);
3238 block->serialize(o, version, true);
3240 // Write block to database
3242 std::string tmp = o.str();
3243 const char *bytes = tmp.c_str();
3245 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3246 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3247 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3248 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3249 int written = sqlite3_step(m_database_write);
3250 if(written != SQLITE_DONE)
3251 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3252 <<sqlite3_errmsg(m_database)<<std::endl;
3253 // Make ready for later reuse
3254 sqlite3_reset(m_database_write);
3256 // We just wrote it to the disk so clear modified flag
3257 block->resetModified();
3260 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3262 DSTACK(__FUNCTION_NAME);
3264 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3267 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3268 if(is.good() == false)
3269 throw FileNotGoodException("Cannot open block file");
3271 v3s16 p3d = getBlockPos(sectordir, blockfile);
3272 v2s16 p2d(p3d.X, p3d.Z);
3274 assert(sector->getPos() == p2d);
3276 u8 version = SER_FMT_VER_INVALID;
3277 is.read((char*)&version, 1);
3280 throw SerializationError("ServerMap::loadBlock(): Failed"
3281 " to read MapBlock version");
3283 /*u32 block_size = MapBlock::serializedLength(version);
3284 SharedBuffer<u8> data(block_size);
3285 is.read((char*)*data, block_size);*/
3287 // This will always return a sector because we're the server
3288 //MapSector *sector = emergeSector(p2d);
3290 MapBlock *block = NULL;
3291 bool created_new = false;
3292 block = sector->getBlockNoCreateNoEx(p3d.Y);
3295 block = sector->createBlankBlockNoInsert(p3d.Y);
3300 block->deSerialize(is, version, true);
3302 // If it's a new block, insert it to the map
3304 sector->insertBlock(block);
3307 Save blocks loaded in old format in new format
3310 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3314 // Should be in database now, so delete the old file
3315 fs::RecursiveDelete(fullpath);
3318 // We just loaded it from the disk, so it's up-to-date.
3319 block->resetModified();
3322 catch(SerializationError &e)
3324 infostream<<"WARNING: Invalid block data on disk "
3325 <<"fullpath="<<fullpath
3326 <<" (SerializationError). "
3327 <<"what()="<<e.what()
3329 //" Ignoring. A new one will be generated.
3332 // TODO: Backup file; name is in fullpath.
3336 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3338 DSTACK(__FUNCTION_NAME);
3341 std::istringstream is(*blob, std::ios_base::binary);
3343 u8 version = SER_FMT_VER_INVALID;
3344 is.read((char*)&version, 1);
3347 throw SerializationError("ServerMap::loadBlock(): Failed"
3348 " to read MapBlock version");
3350 /*u32 block_size = MapBlock::serializedLength(version);
3351 SharedBuffer<u8> data(block_size);
3352 is.read((char*)*data, block_size);*/
3354 // This will always return a sector because we're the server
3355 //MapSector *sector = emergeSector(p2d);
3357 MapBlock *block = NULL;
3358 bool created_new = false;
3359 block = sector->getBlockNoCreateNoEx(p3d.Y);
3362 block = sector->createBlankBlockNoInsert(p3d.Y);
3367 block->deSerialize(is, version, true);
3369 // If it's a new block, insert it to the map
3371 sector->insertBlock(block);
3374 Save blocks loaded in old format in new format
3377 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3378 // Only save if asked to; no need to update version
3382 // We just loaded it from, so it's up-to-date.
3383 block->resetModified();
3386 catch(SerializationError &e)
3388 infostream<<"WARNING: Invalid block data in database "
3389 <<" (SerializationError). "
3390 <<"what()="<<e.what()
3392 //" Ignoring. A new one will be generated.
3395 // TODO: Copy to a backup database.
3399 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3401 DSTACK(__FUNCTION_NAME);
3403 v2s16 p2d(blockpos.X, blockpos.Z);
3405 if(!loadFromFolders()) {
3408 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3409 infostream<<"WARNING: Could not bind block position for load: "
3410 <<sqlite3_errmsg(m_database)<<std::endl;
3411 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3413 Make sure sector is loaded
3415 MapSector *sector = createSector(p2d);
3420 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3421 size_t len = sqlite3_column_bytes(m_database_read, 0);
3423 std::string datastr(data, len);
3425 loadBlock(&datastr, blockpos, sector, false);
3427 sqlite3_step(m_database_read);
3428 // We should never get more than 1 row, so ok to reset
3429 sqlite3_reset(m_database_read);
3431 return getBlockNoCreateNoEx(blockpos);
3433 sqlite3_reset(m_database_read);
3435 // Not found in database, try the files
3438 // The directory layout we're going to load from.
3439 // 1 - original sectors/xxxxzzzz/
3440 // 2 - new sectors2/xxx/zzz/
3441 // If we load from anything but the latest structure, we will
3442 // immediately save to the new one, and remove the old.
3444 std::string sectordir1 = getSectorDir(p2d, 1);
3445 std::string sectordir;
3446 if(fs::PathExists(sectordir1))
3448 sectordir = sectordir1;
3453 sectordir = getSectorDir(p2d, 2);
3457 Make sure sector is loaded
3459 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3463 sector = loadSectorMeta(sectordir, loadlayout != 2);
3465 catch(InvalidFilenameException &e)
3469 catch(FileNotGoodException &e)
3473 catch(std::exception &e)
3480 Make sure file exists
3483 std::string blockfilename = getBlockFilename(blockpos);
3484 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3488 Load block and save it to the database
3490 loadBlock(sectordir, blockfilename, sector, true);
3491 return getBlockNoCreateNoEx(blockpos);
3494 void ServerMap::PrintInfo(std::ostream &out)
3503 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3508 MapVoxelManipulator::~MapVoxelManipulator()
3510 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3514 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3516 TimeTaker timer1("emerge", &emerge_time);
3518 // Units of these are MapBlocks
3519 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3520 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3522 VoxelArea block_area_nodes
3523 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3525 addArea(block_area_nodes);
3527 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3528 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3529 for(s32 x=p_min.X; x<=p_max.X; x++)
3532 core::map<v3s16, bool>::Node *n;
3533 n = m_loaded_blocks.find(p);
3537 bool block_data_inexistent = false;
3540 TimeTaker timer1("emerge load", &emerge_load_time);
3542 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3543 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3545 a.print(infostream);
3546 infostream<<std::endl;*/
3548 MapBlock *block = m_map->getBlockNoCreate(p);
3549 if(block->isDummy())
3550 block_data_inexistent = true;
3552 block->copyTo(*this);
3554 catch(InvalidPositionException &e)
3556 block_data_inexistent = true;
3559 if(block_data_inexistent)
3561 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3562 // Fill with VOXELFLAG_INEXISTENT
3563 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3564 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3566 s32 i = m_area.index(a.MinEdge.X,y,z);
3567 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3571 m_loaded_blocks.insert(p, !block_data_inexistent);
3574 //infostream<<"emerge done"<<std::endl;
3578 SUGG: Add an option to only update eg. water and air nodes.
3579 This will make it interfere less with important stuff if
3582 void MapVoxelManipulator::blitBack
3583 (core::map<v3s16, MapBlock*> & modified_blocks)
3585 if(m_area.getExtent() == v3s16(0,0,0))
3588 //TimeTaker timer1("blitBack");
3590 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3591 <<m_loaded_blocks.size()<<std::endl;*/
3594 Initialize block cache
3596 v3s16 blockpos_last;
3597 MapBlock *block = NULL;
3598 bool block_checked_in_modified = false;
3600 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3601 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3602 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3606 u8 f = m_flags[m_area.index(p)];
3607 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3610 MapNode &n = m_data[m_area.index(p)];
3612 v3s16 blockpos = getNodeBlockPos(p);
3617 if(block == NULL || blockpos != blockpos_last){
3618 block = m_map->getBlockNoCreate(blockpos);
3619 blockpos_last = blockpos;
3620 block_checked_in_modified = false;
3623 // Calculate relative position in block
3624 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3626 // Don't continue if nothing has changed here
3627 if(block->getNode(relpos) == n)
3630 //m_map->setNode(m_area.MinEdge + p, n);
3631 block->setNode(relpos, n);
3634 Make sure block is in modified_blocks
3636 if(block_checked_in_modified == false)
3638 modified_blocks[blockpos] = block;
3639 block_checked_in_modified = true;
3642 catch(InvalidPositionException &e)
3648 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3649 MapVoxelManipulator(map),
3650 m_create_area(false)
3654 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3658 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3660 // Just create the area so that it can be pointed to
3661 VoxelManipulator::emerge(a, caller_id);
3664 void ManualMapVoxelManipulator::initialEmerge(
3665 v3s16 blockpos_min, v3s16 blockpos_max)
3667 TimeTaker timer1("initialEmerge", &emerge_time);
3669 // Units of these are MapBlocks
3670 v3s16 p_min = blockpos_min;
3671 v3s16 p_max = blockpos_max;
3673 VoxelArea block_area_nodes
3674 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3676 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3679 infostream<<"initialEmerge: area: ";
3680 block_area_nodes.print(infostream);
3681 infostream<<" ("<<size_MB<<"MB)";
3682 infostream<<std::endl;
3685 addArea(block_area_nodes);
3687 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3688 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3689 for(s32 x=p_min.X; x<=p_max.X; x++)
3692 core::map<v3s16, bool>::Node *n;
3693 n = m_loaded_blocks.find(p);
3697 bool block_data_inexistent = false;
3700 TimeTaker timer1("emerge load", &emerge_load_time);
3702 MapBlock *block = m_map->getBlockNoCreate(p);
3703 if(block->isDummy())
3704 block_data_inexistent = true;
3706 block->copyTo(*this);
3708 catch(InvalidPositionException &e)
3710 block_data_inexistent = true;
3713 if(block_data_inexistent)
3716 Mark area inexistent
3718 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3719 // Fill with VOXELFLAG_INEXISTENT
3720 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3721 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3723 s32 i = m_area.index(a.MinEdge.X,y,z);
3724 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3728 m_loaded_blocks.insert(p, !block_data_inexistent);
3732 void ManualMapVoxelManipulator::blitBackAll(
3733 core::map<v3s16, MapBlock*> * modified_blocks)
3735 if(m_area.getExtent() == v3s16(0,0,0))
3739 Copy data of all blocks
3741 for(core::map<v3s16, bool>::Iterator
3742 i = m_loaded_blocks.getIterator();
3743 i.atEnd() == false; i++)
3745 v3s16 p = i.getNode()->getKey();
3746 bool existed = i.getNode()->getValue();
3747 if(existed == false)
3749 // The Great Bug was found using this
3750 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3751 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3755 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3758 infostream<<"WARNING: "<<__FUNCTION_NAME
3759 <<": got NULL block "
3760 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3765 block->copyFrom(*this);
3768 modified_blocks->insert(p, block);