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 core::map<v3s16, MapBlock*>::Iterator i;
695 i = a_blocks.getIterator();
696 for(; i.atEnd() == false; i++)
698 MapBlock *block = i.getNode()->getValue();
702 // Don't bother with dummy blocks.
706 v3s16 pos = block->getPos();
707 modified_blocks.insert(pos, block);
709 blocks_to_update.insert(pos, block);
712 Clear all light from block
714 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
715 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
716 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
721 MapNode n = block->getNode(v3s16(x,y,z));
722 u8 oldlight = n.getLight(bank, nodemgr);
723 n.setLight(bank, 0, nodemgr);
724 block->setNode(v3s16(x,y,z), n);
726 // Collect borders for unlighting
727 if(x==0 || x == MAP_BLOCKSIZE-1
728 || y==0 || y == MAP_BLOCKSIZE-1
729 || z==0 || z == MAP_BLOCKSIZE-1)
731 v3s16 p_map = p + v3s16(
734 MAP_BLOCKSIZE*pos.Z);
735 unlight_from.insert(p_map, oldlight);
738 catch(InvalidPositionException &e)
741 This would happen when dealing with a
745 infostream<<"updateLighting(): InvalidPositionException"
750 if(bank == LIGHTBANK_DAY)
752 bool bottom_valid = block->propagateSunlight(light_sources);
754 // If bottom is valid, we're done.
758 else if(bank == LIGHTBANK_NIGHT)
760 // For night lighting, sunlight is not propagated
765 // Invalid lighting bank
769 /*infostream<<"Bottom for sunlight-propagated block ("
770 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
773 // Bottom sunlight is not valid; get the block and loop to it
777 block = getBlockNoCreate(pos);
779 catch(InvalidPositionException &e)
788 Enable this to disable proper lighting for speeding up map
789 generation for testing or whatever
792 //if(g_settings->get(""))
794 core::map<v3s16, MapBlock*>::Iterator i;
795 i = blocks_to_update.getIterator();
796 for(; i.atEnd() == false; i++)
798 MapBlock *block = i.getNode()->getValue();
799 v3s16 p = block->getPos();
800 block->setLightingExpired(false);
808 TimeTaker timer("unspreadLight");
809 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
814 u32 diff = modified_blocks.size() - count_was;
815 count_was = modified_blocks.size();
816 infostream<<"unspreadLight modified "<<diff<<std::endl;
820 TimeTaker timer("spreadLight");
821 spreadLight(bank, light_sources, modified_blocks);
826 u32 diff = modified_blocks.size() - count_was;
827 count_was = modified_blocks.size();
828 infostream<<"spreadLight modified "<<diff<<std::endl;
833 //MapVoxelManipulator vmanip(this);
835 // Make a manual voxel manipulator and load all the blocks
836 // that touch the requested blocks
837 ManualMapVoxelManipulator vmanip(this);
838 core::map<v3s16, MapBlock*>::Iterator i;
839 i = blocks_to_update.getIterator();
840 for(; i.atEnd() == false; i++)
842 MapBlock *block = i.getNode()->getValue();
843 v3s16 p = block->getPos();
845 // Add all surrounding blocks
846 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
849 Add all surrounding blocks that have up-to-date lighting
850 NOTE: This doesn't quite do the job (not everything
851 appropriate is lighted)
853 /*for(s16 z=-1; z<=1; z++)
854 for(s16 y=-1; y<=1; y++)
855 for(s16 x=-1; x<=1; x++)
857 v3s16 p2 = p + v3s16(x,y,z);
858 MapBlock *block = getBlockNoCreateNoEx(p2);
863 if(block->getLightingExpired())
865 vmanip.initialEmerge(p2, p2);
868 // Lighting of block will be updated completely
869 block->setLightingExpired(false);
873 //TimeTaker timer("unSpreadLight");
874 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
877 //TimeTaker timer("spreadLight");
878 vmanip.spreadLight(bank, light_sources, nodemgr);
881 //TimeTaker timer("blitBack");
882 vmanip.blitBack(modified_blocks);
884 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
888 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
891 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
892 core::map<v3s16, MapBlock*> & modified_blocks)
894 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
895 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
898 Update information about whether day and night light differ
900 for(core::map<v3s16, MapBlock*>::Iterator
901 i = modified_blocks.getIterator();
902 i.atEnd() == false; i++)
904 MapBlock *block = i.getNode()->getValue();
905 block->updateDayNightDiff();
911 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
912 core::map<v3s16, MapBlock*> &modified_blocks)
914 INodeDefManager *nodemgr = m_gamedef->ndef();
917 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
918 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
921 From this node to nodes underneath:
922 If lighting is sunlight (1.0), unlight neighbours and
927 v3s16 toppos = p + v3s16(0,1,0);
928 v3s16 bottompos = p + v3s16(0,-1,0);
930 bool node_under_sunlight = true;
931 core::map<v3s16, bool> light_sources;
934 If there is a node at top and it doesn't have sunlight,
935 there has not been any sunlight going down.
937 Otherwise there probably is.
940 MapNode topnode = getNode(toppos);
942 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
943 node_under_sunlight = false;
945 catch(InvalidPositionException &e)
950 Remove all light that has come out of this node
953 enum LightBank banks[] =
958 for(s32 i=0; i<2; i++)
960 enum LightBank bank = banks[i];
962 u8 lightwas = getNode(p).getLight(bank, nodemgr);
964 // Add the block of the added node to modified_blocks
965 v3s16 blockpos = getNodeBlockPos(p);
966 MapBlock * block = getBlockNoCreate(blockpos);
967 assert(block != NULL);
968 modified_blocks.insert(blockpos, block);
970 assert(isValidPosition(p));
972 // Unlight neighbours of node.
973 // This means setting light of all consequent dimmer nodes
975 // This also collects the nodes at the border which will spread
976 // light again into this.
977 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
979 n.setLight(bank, 0, nodemgr);
983 If node lets sunlight through and is under sunlight, it has
986 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
988 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
992 Set the node on the map
1001 std::string metadata_name = nodemgr->get(n).metadata_name;
1002 if(metadata_name != ""){
1003 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1005 errorstream<<"Failed to create node metadata \""
1006 <<metadata_name<<"\""<<std::endl;
1008 setNodeMetadata(p, meta);
1013 If node is under sunlight and doesn't let sunlight through,
1014 take all sunlighted nodes under it and clear light from them
1015 and from where the light has been spread.
1016 TODO: This could be optimized by mass-unlighting instead
1019 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1023 //m_dout<<DTIME<<"y="<<y<<std::endl;
1024 v3s16 n2pos(p.X, y, p.Z);
1028 n2 = getNode(n2pos);
1030 catch(InvalidPositionException &e)
1035 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1037 unLightNeighbors(LIGHTBANK_DAY,
1038 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1039 light_sources, modified_blocks);
1040 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1048 for(s32 i=0; i<2; i++)
1050 enum LightBank bank = banks[i];
1053 Spread light from all nodes that might be capable of doing so
1055 spreadLight(bank, light_sources, modified_blocks);
1059 Update information about whether day and night light differ
1061 for(core::map<v3s16, MapBlock*>::Iterator
1062 i = modified_blocks.getIterator();
1063 i.atEnd() == false; i++)
1065 MapBlock *block = i.getNode()->getValue();
1066 block->updateDayNightDiff();
1070 Add neighboring liquid nodes and the node itself if it is
1071 liquid (=water node was added) to transform queue.
1074 v3s16(0,0,0), // self
1075 v3s16(0,0,1), // back
1076 v3s16(0,1,0), // top
1077 v3s16(1,0,0), // right
1078 v3s16(0,0,-1), // front
1079 v3s16(0,-1,0), // bottom
1080 v3s16(-1,0,0), // left
1082 for(u16 i=0; i<7; i++)
1087 v3s16 p2 = p + dirs[i];
1089 MapNode n2 = getNode(p2);
1090 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1092 m_transforming_liquid.push_back(p2);
1095 }catch(InvalidPositionException &e)
1103 void Map::removeNodeAndUpdate(v3s16 p,
1104 core::map<v3s16, MapBlock*> &modified_blocks)
1106 INodeDefManager *nodemgr = m_gamedef->ndef();
1108 /*PrintInfo(m_dout);
1109 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1110 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1112 bool node_under_sunlight = true;
1114 v3s16 toppos = p + v3s16(0,1,0);
1116 // Node will be replaced with this
1117 content_t replace_material = CONTENT_AIR;
1120 If there is a node at top and it doesn't have sunlight,
1121 there will be no sunlight going down.
1124 MapNode topnode = getNode(toppos);
1126 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1127 node_under_sunlight = false;
1129 catch(InvalidPositionException &e)
1133 core::map<v3s16, bool> light_sources;
1135 enum LightBank banks[] =
1140 for(s32 i=0; i<2; i++)
1142 enum LightBank bank = banks[i];
1145 Unlight neighbors (in case the node is a light source)
1147 unLightNeighbors(bank, p,
1148 getNode(p).getLight(bank, nodemgr),
1149 light_sources, modified_blocks);
1153 Remove node metadata
1156 removeNodeMetadata(p);
1160 This also clears the lighting.
1164 n.setContent(replace_material);
1167 for(s32 i=0; i<2; i++)
1169 enum LightBank bank = banks[i];
1172 Recalculate lighting
1174 spreadLight(bank, light_sources, modified_blocks);
1177 // Add the block of the removed node to modified_blocks
1178 v3s16 blockpos = getNodeBlockPos(p);
1179 MapBlock * block = getBlockNoCreate(blockpos);
1180 assert(block != NULL);
1181 modified_blocks.insert(blockpos, block);
1184 If the removed node was under sunlight, propagate the
1185 sunlight down from it and then light all neighbors
1186 of the propagated blocks.
1188 if(node_under_sunlight)
1190 s16 ybottom = propagateSunlight(p, modified_blocks);
1191 /*m_dout<<DTIME<<"Node was under sunlight. "
1192 "Propagating sunlight";
1193 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1195 for(; y >= ybottom; y--)
1197 v3s16 p2(p.X, y, p.Z);
1198 /*m_dout<<DTIME<<"lighting neighbors of node ("
1199 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1201 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1206 // Set the lighting of this node to 0
1207 // TODO: Is this needed? Lighting is cleared up there already.
1209 MapNode n = getNode(p);
1210 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1213 catch(InvalidPositionException &e)
1219 for(s32 i=0; i<2; i++)
1221 enum LightBank bank = banks[i];
1223 // Get the brightest neighbour node and propagate light from it
1224 v3s16 n2p = getBrightestNeighbour(bank, p);
1226 MapNode n2 = getNode(n2p);
1227 lightNeighbors(bank, n2p, modified_blocks);
1229 catch(InvalidPositionException &e)
1235 Update information about whether day and night light differ
1237 for(core::map<v3s16, MapBlock*>::Iterator
1238 i = modified_blocks.getIterator();
1239 i.atEnd() == false; i++)
1241 MapBlock *block = i.getNode()->getValue();
1242 block->updateDayNightDiff();
1246 Add neighboring liquid nodes and this node to transform queue.
1247 (it's vital for the node itself to get updated last.)
1250 v3s16(0,0,1), // back
1251 v3s16(0,1,0), // top
1252 v3s16(1,0,0), // right
1253 v3s16(0,0,-1), // front
1254 v3s16(0,-1,0), // bottom
1255 v3s16(-1,0,0), // left
1256 v3s16(0,0,0), // self
1258 for(u16 i=0; i<7; i++)
1263 v3s16 p2 = p + dirs[i];
1265 MapNode n2 = getNode(p2);
1266 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1268 m_transforming_liquid.push_back(p2);
1271 }catch(InvalidPositionException &e)
1277 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1280 event.type = MEET_ADDNODE;
1284 bool succeeded = true;
1286 core::map<v3s16, MapBlock*> modified_blocks;
1287 addNodeAndUpdate(p, n, modified_blocks);
1289 // Copy modified_blocks to event
1290 for(core::map<v3s16, MapBlock*>::Iterator
1291 i = modified_blocks.getIterator();
1292 i.atEnd()==false; i++)
1294 event.modified_blocks.insert(i.getNode()->getKey(), false);
1297 catch(InvalidPositionException &e){
1301 dispatchEvent(&event);
1306 bool Map::removeNodeWithEvent(v3s16 p)
1309 event.type = MEET_REMOVENODE;
1312 bool succeeded = true;
1314 core::map<v3s16, MapBlock*> modified_blocks;
1315 removeNodeAndUpdate(p, modified_blocks);
1317 // Copy modified_blocks to event
1318 for(core::map<v3s16, MapBlock*>::Iterator
1319 i = modified_blocks.getIterator();
1320 i.atEnd()==false; i++)
1322 event.modified_blocks.insert(i.getNode()->getKey(), false);
1325 catch(InvalidPositionException &e){
1329 dispatchEvent(&event);
1334 bool Map::dayNightDiffed(v3s16 blockpos)
1337 v3s16 p = blockpos + v3s16(0,0,0);
1338 MapBlock *b = getBlockNoCreate(p);
1339 if(b->dayNightDiffed())
1342 catch(InvalidPositionException &e){}
1345 v3s16 p = blockpos + v3s16(-1,0,0);
1346 MapBlock *b = getBlockNoCreate(p);
1347 if(b->dayNightDiffed())
1350 catch(InvalidPositionException &e){}
1352 v3s16 p = blockpos + v3s16(0,-1,0);
1353 MapBlock *b = getBlockNoCreate(p);
1354 if(b->dayNightDiffed())
1357 catch(InvalidPositionException &e){}
1359 v3s16 p = blockpos + v3s16(0,0,-1);
1360 MapBlock *b = getBlockNoCreate(p);
1361 if(b->dayNightDiffed())
1364 catch(InvalidPositionException &e){}
1367 v3s16 p = blockpos + v3s16(1,0,0);
1368 MapBlock *b = getBlockNoCreate(p);
1369 if(b->dayNightDiffed())
1372 catch(InvalidPositionException &e){}
1374 v3s16 p = blockpos + v3s16(0,1,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->dayNightDiffed())
1379 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(0,0,1);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->dayNightDiffed())
1386 catch(InvalidPositionException &e){}
1392 Updates usage timers
1394 void Map::timerUpdate(float dtime, float unload_timeout,
1395 core::list<v3s16> *unloaded_blocks)
1397 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1399 // Profile modified reasons
1400 Profiler modprofiler;
1402 core::list<v2s16> sector_deletion_queue;
1403 u32 deleted_blocks_count = 0;
1404 u32 saved_blocks_count = 0;
1405 u32 block_count_all = 0;
1407 core::map<v2s16, MapSector*>::Iterator si;
1410 si = m_sectors.getIterator();
1411 for(; si.atEnd() == false; si++)
1413 MapSector *sector = si.getNode()->getValue();
1415 bool all_blocks_deleted = true;
1417 core::list<MapBlock*> blocks;
1418 sector->getBlocks(blocks);
1420 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1421 i != blocks.end(); i++)
1423 MapBlock *block = (*i);
1425 block->incrementUsageTimer(dtime);
1427 if(block->getUsageTimer() > unload_timeout)
1429 v3s16 p = block->getPos();
1432 if(block->getModified() != MOD_STATE_CLEAN
1433 && save_before_unloading)
1435 modprofiler.add(block->getModifiedReason(), 1);
1437 saved_blocks_count++;
1440 // Delete from memory
1441 sector->deleteBlock(block);
1444 unloaded_blocks->push_back(p);
1446 deleted_blocks_count++;
1450 all_blocks_deleted = false;
1455 if(all_blocks_deleted)
1457 sector_deletion_queue.push_back(si.getNode()->getKey());
1462 // Finally delete the empty sectors
1463 deleteSectors(sector_deletion_queue);
1465 if(deleted_blocks_count != 0)
1467 PrintInfo(infostream); // ServerMap/ClientMap:
1468 infostream<<"Unloaded "<<deleted_blocks_count
1469 <<" blocks from memory";
1470 if(save_before_unloading)
1471 infostream<<", of which "<<saved_blocks_count<<" were written";
1472 infostream<<", "<<block_count_all<<" blocks in memory";
1473 infostream<<"."<<std::endl;
1474 if(saved_blocks_count != 0){
1475 PrintInfo(infostream); // ServerMap/ClientMap:
1476 infostream<<"Blocks modified by: "<<std::endl;
1477 modprofiler.print(infostream);
1482 void Map::deleteSectors(core::list<v2s16> &list)
1484 core::list<v2s16>::Iterator j;
1485 for(j=list.begin(); j!=list.end(); j++)
1487 MapSector *sector = m_sectors[*j];
1488 // If sector is in sector cache, remove it from there
1489 if(m_sector_cache == sector)
1490 m_sector_cache = NULL;
1491 // Remove from map and delete
1492 m_sectors.remove(*j);
1498 void Map::unloadUnusedData(float timeout,
1499 core::list<v3s16> *deleted_blocks)
1501 core::list<v2s16> sector_deletion_queue;
1502 u32 deleted_blocks_count = 0;
1503 u32 saved_blocks_count = 0;
1505 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1506 for(; si.atEnd() == false; si++)
1508 MapSector *sector = si.getNode()->getValue();
1510 bool all_blocks_deleted = true;
1512 core::list<MapBlock*> blocks;
1513 sector->getBlocks(blocks);
1514 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1515 i != blocks.end(); i++)
1517 MapBlock *block = (*i);
1519 if(block->getUsageTimer() > timeout)
1522 if(block->getModified() != MOD_STATE_CLEAN)
1525 saved_blocks_count++;
1527 // Delete from memory
1528 sector->deleteBlock(block);
1529 deleted_blocks_count++;
1533 all_blocks_deleted = false;
1537 if(all_blocks_deleted)
1539 sector_deletion_queue.push_back(si.getNode()->getKey());
1543 deleteSectors(sector_deletion_queue);
1545 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1546 <<", of which "<<saved_blocks_count<<" were wr."
1549 //return sector_deletion_queue.getSize();
1550 //return deleted_blocks_count;
1554 void Map::PrintInfo(std::ostream &out)
1559 #define WATER_DROP_BOOST 4
1563 NEIGHBOR_SAME_LEVEL,
1566 struct NodeNeighbor {
1572 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1574 INodeDefManager *nodemgr = m_gamedef->ndef();
1576 DSTACK(__FUNCTION_NAME);
1577 //TimeTaker timer("transformLiquids()");
1580 u32 initial_size = m_transforming_liquid.size();
1582 /*if(initial_size != 0)
1583 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1585 // list of nodes that due to viscosity have not reached their max level height
1586 UniqueQueue<v3s16> must_reflow;
1588 // List of MapBlocks that will require a lighting update (due to lava)
1589 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1591 while(m_transforming_liquid.size() != 0)
1593 // This should be done here so that it is done when continue is used
1594 if(loopcount >= initial_size * 3)
1599 Get a queued transforming liquid node
1601 v3s16 p0 = m_transforming_liquid.pop_front();
1603 MapNode n0 = getNodeNoEx(p0);
1606 Collect information about current node
1608 s8 liquid_level = -1;
1609 u8 liquid_kind = CONTENT_IGNORE;
1610 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1611 switch (liquid_type) {
1613 liquid_level = LIQUID_LEVEL_SOURCE;
1614 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1616 case LIQUID_FLOWING:
1617 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1618 liquid_kind = n0.getContent();
1621 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1622 // continue with the next node.
1623 if (n0.getContent() != CONTENT_AIR)
1625 liquid_kind = CONTENT_AIR;
1630 Collect information about the environment
1632 const v3s16 *dirs = g_6dirs;
1633 NodeNeighbor sources[6]; // surrounding sources
1634 int num_sources = 0;
1635 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1637 NodeNeighbor airs[6]; // surrounding air
1639 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1640 int num_neutrals = 0;
1641 bool flowing_down = false;
1642 for (u16 i = 0; i < 6; i++) {
1643 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1646 nt = NEIGHBOR_UPPER;
1649 nt = NEIGHBOR_LOWER;
1652 v3s16 npos = p0 + dirs[i];
1653 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1654 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1656 if (nb.n.getContent() == CONTENT_AIR) {
1657 airs[num_airs++] = nb;
1658 // if the current node is a water source the neighbor
1659 // should be enqueded for transformation regardless of whether the
1660 // current node changes or not.
1661 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1662 m_transforming_liquid.push_back(npos);
1663 // if the current node happens to be a flowing node, it will start to flow down here.
1664 if (nb.t == NEIGHBOR_LOWER) {
1665 flowing_down = true;
1668 neutrals[num_neutrals++] = nb;
1672 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1673 if (liquid_kind == CONTENT_AIR)
1674 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1675 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1676 neutrals[num_neutrals++] = nb;
1678 // Do not count bottom source, it will screw things up
1680 sources[num_sources++] = nb;
1683 case LIQUID_FLOWING:
1684 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1685 if (liquid_kind == CONTENT_AIR)
1686 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1687 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1688 neutrals[num_neutrals++] = nb;
1690 flows[num_flows++] = nb;
1691 if (nb.t == NEIGHBOR_LOWER)
1692 flowing_down = true;
1699 decide on the type (and possibly level) of the current node
1701 content_t new_node_content;
1702 s8 new_node_level = -1;
1703 s8 max_node_level = -1;
1704 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1705 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1706 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1707 // it's perfectly safe to use liquid_kind here to determine the new node content.
1708 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1709 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1710 // liquid_kind is set properly, see above
1711 new_node_content = liquid_kind;
1712 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1714 // no surrounding sources, so get the maximum level that can flow into this node
1715 for (u16 i = 0; i < num_flows; i++) {
1716 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1717 switch (flows[i].t) {
1718 case NEIGHBOR_UPPER:
1719 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1720 max_node_level = LIQUID_LEVEL_MAX;
1721 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1722 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1723 } else if (nb_liquid_level > max_node_level)
1724 max_node_level = nb_liquid_level;
1726 case NEIGHBOR_LOWER:
1728 case NEIGHBOR_SAME_LEVEL:
1729 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1730 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1731 max_node_level = nb_liquid_level - 1;
1737 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1738 if (viscosity > 1 && max_node_level != liquid_level) {
1739 // amount to gain, limited by viscosity
1740 // must be at least 1 in absolute value
1741 s8 level_inc = max_node_level - liquid_level;
1742 if (level_inc < -viscosity || level_inc > viscosity)
1743 new_node_level = liquid_level + level_inc/viscosity;
1744 else if (level_inc < 0)
1745 new_node_level = liquid_level - 1;
1746 else if (level_inc > 0)
1747 new_node_level = liquid_level + 1;
1748 if (new_node_level != max_node_level)
1749 must_reflow.push_back(p0);
1751 new_node_level = max_node_level;
1753 if (new_node_level >= 0)
1754 new_node_content = liquid_kind;
1756 new_node_content = CONTENT_AIR;
1761 check if anything has changed. if not, just continue with the next node.
1763 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1764 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1765 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1771 update the current node
1773 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1774 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1775 // set level to last 3 bits, flowing down bit to 4th bit
1776 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1778 // set the liquid level and flow bit to 0
1779 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1781 n0.setContent(new_node_content);
1783 v3s16 blockpos = getNodeBlockPos(p0);
1784 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1786 modified_blocks.insert(blockpos, block);
1787 // If node emits light, MapBlock requires lighting update
1788 if(nodemgr->get(n0).light_source != 0)
1789 lighting_modified_blocks[block->getPos()] = block;
1793 enqueue neighbors for update if neccessary
1795 switch (nodemgr->get(n0.getContent()).liquid_type) {
1797 case LIQUID_FLOWING:
1798 // make sure source flows into all neighboring nodes
1799 for (u16 i = 0; i < num_flows; i++)
1800 if (flows[i].t != NEIGHBOR_UPPER)
1801 m_transforming_liquid.push_back(flows[i].p);
1802 for (u16 i = 0; i < num_airs; i++)
1803 if (airs[i].t != NEIGHBOR_UPPER)
1804 m_transforming_liquid.push_back(airs[i].p);
1807 // this flow has turned to air; neighboring flows might need to do the same
1808 for (u16 i = 0; i < num_flows; i++)
1809 m_transforming_liquid.push_back(flows[i].p);
1813 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1814 while (must_reflow.size() > 0)
1815 m_transforming_liquid.push_back(must_reflow.pop_front());
1816 updateLighting(lighting_modified_blocks, modified_blocks);
1819 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1821 v3s16 blockpos = getNodeBlockPos(p);
1822 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1823 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1825 infostream<<"Map::getNodeMetadata(): Need to emerge "
1826 <<PP(blockpos)<<std::endl;
1827 block = emergeBlock(blockpos, false);
1831 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1835 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1839 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1841 v3s16 blockpos = getNodeBlockPos(p);
1842 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1843 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1845 infostream<<"Map::setNodeMetadata(): Need to emerge "
1846 <<PP(blockpos)<<std::endl;
1847 block = emergeBlock(blockpos, false);
1851 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1855 block->m_node_metadata->set(p_rel, meta);
1858 void Map::removeNodeMetadata(v3s16 p)
1860 v3s16 blockpos = getNodeBlockPos(p);
1861 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1862 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1865 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1869 block->m_node_metadata->remove(p_rel);
1872 void Map::nodeMetadataStep(float dtime,
1873 core::map<v3s16, MapBlock*> &changed_blocks)
1877 Currently there is no way to ensure that all the necessary
1878 blocks are loaded when this is run. (They might get unloaded)
1879 NOTE: ^- Actually, that might not be so. In a quick test it
1880 reloaded a block with a furnace when I walked back to it from
1883 core::map<v2s16, MapSector*>::Iterator si;
1884 si = m_sectors.getIterator();
1885 for(; si.atEnd() == false; si++)
1887 MapSector *sector = si.getNode()->getValue();
1888 core::list< MapBlock * > sectorblocks;
1889 sector->getBlocks(sectorblocks);
1890 core::list< MapBlock * >::Iterator i;
1891 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1893 MapBlock *block = *i;
1894 bool changed = block->m_node_metadata->step(dtime);
1896 changed_blocks[block->getPos()] = block;
1905 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1906 Map(dout_server, gamedef),
1908 m_map_metadata_changed(true),
1910 m_database_read(NULL),
1911 m_database_write(NULL)
1913 verbosestream<<__FUNCTION_NAME<<std::endl;
1915 //m_chunksize = 8; // Takes a few seconds
1917 if (g_settings->get("fixed_map_seed").empty())
1919 m_seed = (((u64)(myrand()%0xffff)<<0)
1920 + ((u64)(myrand()%0xffff)<<16)
1921 + ((u64)(myrand()%0xffff)<<32)
1922 + ((u64)(myrand()%0xffff)<<48));
1926 m_seed = g_settings->getU64("fixed_map_seed");
1930 Experimental and debug stuff
1937 Try to load map; if not found, create a new one.
1940 m_savedir = savedir;
1941 m_map_saving_enabled = false;
1945 // If directory exists, check contents and load if possible
1946 if(fs::PathExists(m_savedir))
1948 // If directory is empty, it is safe to save into it.
1949 if(fs::GetDirListing(m_savedir).size() == 0)
1951 infostream<<"ServerMap: Empty save directory is valid."
1953 m_map_saving_enabled = true;
1958 // Load map metadata (seed, chunksize)
1961 catch(FileNotGoodException &e){
1962 infostream<<"WARNING: Could not load map metadata"
1963 //<<" Disabling chunk-based generator."
1968 infostream<<"ServerMap: Successfully loaded map "
1969 <<"metadata from "<<savedir
1970 <<", assuming valid save directory."
1971 <<" seed="<<m_seed<<"."
1974 m_map_saving_enabled = true;
1975 // Map loaded, not creating new one
1979 // If directory doesn't exist, it is safe to save to it
1981 m_map_saving_enabled = true;
1984 catch(std::exception &e)
1986 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
1987 <<", exception: "<<e.what()<<std::endl;
1988 infostream<<"Please remove the map or fix it."<<std::endl;
1989 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1992 infostream<<"Initializing new map."<<std::endl;
1994 // Create zero sector
1995 emergeSector(v2s16(0,0));
1997 // Initially write whole map
1998 save(MOD_STATE_CLEAN);
2001 ServerMap::~ServerMap()
2003 verbosestream<<__FUNCTION_NAME<<std::endl;
2007 if(m_map_saving_enabled)
2009 // Save only changed parts
2010 save(MOD_STATE_WRITE_AT_UNLOAD);
2011 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2015 infostream<<"ServerMap: Map not saved"<<std::endl;
2018 catch(std::exception &e)
2020 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2021 <<", exception: "<<e.what()<<std::endl;
2025 Close database if it was opened
2028 sqlite3_finalize(m_database_read);
2029 if(m_database_write)
2030 sqlite3_finalize(m_database_write);
2032 sqlite3_close(m_database);
2038 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2039 for(; i.atEnd() == false; i++)
2041 MapChunk *chunk = i.getNode()->getValue();
2047 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2049 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2050 if(enable_mapgen_debug_info)
2051 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2052 <<blockpos.Z<<")"<<std::endl;
2054 // Do nothing if not inside limits (+-1 because of neighbors)
2055 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2056 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2062 data->no_op = false;
2063 data->seed = m_seed;
2064 data->blockpos = blockpos;
2065 data->nodedef = m_gamedef->ndef();
2068 Create the whole area of this and the neighboring blocks
2071 //TimeTaker timer("initBlockMake() create area");
2073 for(s16 x=-1; x<=1; x++)
2074 for(s16 z=-1; z<=1; z++)
2076 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2077 // Sector metadata is loaded from disk if not already loaded.
2078 ServerMapSector *sector = createSector(sectorpos);
2081 for(s16 y=-1; y<=1; y++)
2083 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2084 //MapBlock *block = createBlock(p);
2085 // 1) get from memory, 2) load from disk
2086 MapBlock *block = emergeBlock(p, false);
2087 // 3) create a blank one
2090 block = createBlock(p);
2093 Block gets sunlight if this is true.
2095 Refer to the map generator heuristics.
2097 bool ug = mapgen::block_is_underground(data->seed, p);
2098 block->setIsUnderground(ug);
2101 // Lighting will not be valid after make_chunk is called
2102 block->setLightingExpired(true);
2103 // Lighting will be calculated
2104 //block->setLightingExpired(false);
2110 Now we have a big empty area.
2112 Make a ManualMapVoxelManipulator that contains this and the
2116 // The area that contains this block and it's neighbors
2117 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2118 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2120 data->vmanip = new ManualMapVoxelManipulator(this);
2121 //data->vmanip->setMap(this);
2125 //TimeTaker timer("initBlockMake() initialEmerge");
2126 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2129 // Data is ready now.
2132 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2133 core::map<v3s16, MapBlock*> &changed_blocks)
2135 v3s16 blockpos = data->blockpos;
2136 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2137 <<blockpos.Z<<")"<<std::endl;*/
2141 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2145 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2147 /*infostream<<"Resulting vmanip:"<<std::endl;
2148 data->vmanip.print(infostream);*/
2150 // Make sure affected blocks are loaded
2151 for(s16 x=-1; x<=1; x++)
2152 for(s16 z=-1; z<=1; z++)
2153 for(s16 y=-1; y<=1; y++)
2155 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2156 // Load from disk if not already in memory
2157 emergeBlock(p, false);
2161 Blit generated stuff to map
2162 NOTE: blitBackAll adds nearly everything to changed_blocks
2166 //TimeTaker timer("finishBlockMake() blitBackAll");
2167 data->vmanip->blitBackAll(&changed_blocks);
2170 if(enable_mapgen_debug_info)
2171 infostream<<"finishBlockMake: changed_blocks.size()="
2172 <<changed_blocks.size()<<std::endl;
2175 Copy transforming liquid information
2177 while(data->transforming_liquid.size() > 0)
2179 v3s16 p = data->transforming_liquid.pop_front();
2180 m_transforming_liquid.push_back(p);
2186 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2190 Set is_underground flag for lighting with sunlight.
2192 Refer to map generator heuristics.
2194 NOTE: This is done in initChunkMake
2196 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2200 Add sunlight to central block.
2201 This makes in-dark-spawning monsters to not flood the whole thing.
2202 Do not spread the light, though.
2204 /*core::map<v3s16, bool> light_sources;
2205 bool black_air_left = false;
2206 block->propagateSunlight(light_sources, true, &black_air_left);*/
2209 NOTE: Lighting and object adding shouldn't really be here, but
2210 lighting is a bit tricky to move properly to makeBlock.
2211 TODO: Do this the right way anyway, that is, move it to makeBlock.
2212 - There needs to be some way for makeBlock to report back if
2213 the lighting update is going further down because of the
2214 new block blocking light
2219 NOTE: This takes ~60ms, TODO: Investigate why
2222 TimeTaker t("finishBlockMake lighting update");
2224 core::map<v3s16, MapBlock*> lighting_update_blocks;
2227 lighting_update_blocks.insert(block->getPos(), block);
2232 v3s16 p = block->getPos()+v3s16(x,1,z);
2233 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2237 // All modified blocks
2238 // NOTE: Should this be done? If this is not done, then the lighting
2239 // of the others will be updated in a different place, one by one, i
2240 // think... or they might not? Well, at least they are left marked as
2241 // "lighting expired"; it seems that is not handled at all anywhere,
2242 // so enabling this will slow it down A LOT because otherwise it
2243 // would not do this at all. This causes the black trees.
2244 for(core::map<v3s16, MapBlock*>::Iterator
2245 i = changed_blocks.getIterator();
2246 i.atEnd() == false; i++)
2248 lighting_update_blocks.insert(i.getNode()->getKey(),
2249 i.getNode()->getValue());
2251 /*// Also force-add all the upmost blocks for proper sunlight
2252 for(s16 x=-1; x<=1; x++)
2253 for(s16 z=-1; z<=1; z++)
2255 v3s16 p = block->getPos()+v3s16(x,1,z);
2256 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2259 updateLighting(lighting_update_blocks, changed_blocks);
2262 Set lighting to non-expired state in all of them.
2263 This is cheating, but it is not fast enough if all of them
2264 would actually be updated.
2266 for(s16 x=-1; x<=1; x++)
2267 for(s16 y=-1; y<=1; y++)
2268 for(s16 z=-1; z<=1; z++)
2270 v3s16 p = block->getPos()+v3s16(x,y,z);
2271 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2274 if(enable_mapgen_debug_info == false)
2275 t.stop(true); // Hide output
2279 Add random objects to block
2281 mapgen::add_random_objects(block);
2284 Go through changed blocks
2286 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2287 i.atEnd() == false; i++)
2289 MapBlock *block = i.getNode()->getValue();
2292 Update day/night difference cache of the MapBlocks
2294 block->updateDayNightDiff();
2296 Set block as modified
2298 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2299 "finishBlockMake updateDayNightDiff");
2303 Set central block as generated
2305 block->setGenerated(true);
2308 Save changed parts of map
2309 NOTE: Will be saved later.
2311 //save(MOD_STATE_WRITE_AT_UNLOAD);
2313 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2314 <<blockpos.Z<<")"<<std::endl;*/
2316 if(enable_mapgen_debug_info)
2319 Analyze resulting blocks
2321 for(s16 x=-1; x<=1; x++)
2322 for(s16 y=-1; y<=1; y++)
2323 for(s16 z=-1; z<=1; z++)
2325 v3s16 p = block->getPos()+v3s16(x,y,z);
2326 MapBlock *block = getBlockNoCreateNoEx(p);
2328 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2329 infostream<<"Generated "<<spos<<": "
2330 <<analyze_block(block)<<std::endl;
2338 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2340 DSTACKF("%s: p2d=(%d,%d)",
2345 Check if it exists already in memory
2347 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2352 Try to load it from disk (with blocks)
2354 //if(loadSectorFull(p2d) == true)
2357 Try to load metadata from disk
2360 if(loadSectorMeta(p2d) == true)
2362 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2365 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2366 throw InvalidPositionException("");
2372 Do not create over-limit
2374 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2375 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2376 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2377 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2378 throw InvalidPositionException("createSector(): pos. over limit");
2381 Generate blank sector
2384 sector = new ServerMapSector(this, p2d, m_gamedef);
2386 // Sector position on map in nodes
2387 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2392 m_sectors.insert(p2d, sector);
2398 This is a quick-hand function for calling makeBlock().
2400 MapBlock * ServerMap::generateBlock(
2402 core::map<v3s16, MapBlock*> &modified_blocks
2405 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2407 /*infostream<<"generateBlock(): "
2408 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2411 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2413 TimeTaker timer("generateBlock");
2415 //MapBlock *block = original_dummy;
2417 v2s16 p2d(p.X, p.Z);
2418 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2421 Do not generate over-limit
2423 if(blockpos_over_limit(p))
2425 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2426 throw InvalidPositionException("generateBlock(): pos. over limit");
2430 Create block make data
2432 mapgen::BlockMakeData data;
2433 initBlockMake(&data, p);
2439 TimeTaker t("mapgen::make_block()");
2440 mapgen::make_block(&data);
2442 if(enable_mapgen_debug_info == false)
2443 t.stop(true); // Hide output
2447 Blit data back on map, update lighting, add mobs and whatever this does
2449 finishBlockMake(&data, modified_blocks);
2454 MapBlock *block = getBlockNoCreateNoEx(p);
2462 bool erroneus_content = false;
2463 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2464 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2465 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2468 MapNode n = block->getNode(p);
2469 if(n.getContent() == CONTENT_IGNORE)
2471 infostream<<"CONTENT_IGNORE at "
2472 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2474 erroneus_content = true;
2478 if(erroneus_content)
2487 Generate a completely empty block
2491 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2492 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2494 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2497 n.setContent(CONTENT_AIR);
2498 block->setNode(v3s16(x0,y0,z0), n);
2504 if(enable_mapgen_debug_info == false)
2505 timer.stop(true); // Hide output
2510 MapBlock * ServerMap::createBlock(v3s16 p)
2512 DSTACKF("%s: p=(%d,%d,%d)",
2513 __FUNCTION_NAME, p.X, p.Y, p.Z);
2516 Do not create over-limit
2518 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2519 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2520 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2521 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2522 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2523 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2524 throw InvalidPositionException("createBlock(): pos. over limit");
2526 v2s16 p2d(p.X, p.Z);
2529 This will create or load a sector if not found in memory.
2530 If block exists on disk, it will be loaded.
2532 NOTE: On old save formats, this will be slow, as it generates
2533 lighting on blocks for them.
2535 ServerMapSector *sector;
2537 sector = (ServerMapSector*)createSector(p2d);
2538 assert(sector->getId() == MAPSECTOR_SERVER);
2540 catch(InvalidPositionException &e)
2542 infostream<<"createBlock: createSector() failed"<<std::endl;
2546 NOTE: This should not be done, or at least the exception
2547 should not be passed on as std::exception, because it
2548 won't be catched at all.
2550 /*catch(std::exception &e)
2552 infostream<<"createBlock: createSector() failed: "
2553 <<e.what()<<std::endl;
2558 Try to get a block from the sector
2561 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2564 if(block->isDummy())
2569 block = sector->createBlankBlock(block_y);
2573 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2575 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2577 p.X, p.Y, p.Z, allow_generate);
2580 MapBlock *block = getBlockNoCreateNoEx(p);
2581 if(block && block->isDummy() == false)
2586 MapBlock *block = loadBlock(p);
2593 core::map<v3s16, MapBlock*> modified_blocks;
2594 MapBlock *block = generateBlock(p, modified_blocks);
2598 event.type = MEET_OTHER;
2601 // Copy modified_blocks to event
2602 for(core::map<v3s16, MapBlock*>::Iterator
2603 i = modified_blocks.getIterator();
2604 i.atEnd()==false; i++)
2606 event.modified_blocks.insert(i.getNode()->getKey(), false);
2610 dispatchEvent(&event);
2619 s16 ServerMap::findGroundLevel(v2s16 p2d)
2623 Uh, just do something random...
2625 // Find existing map from top to down
2628 v3s16 p(p2d.X, max, p2d.Y);
2629 for(; p.Y>min; p.Y--)
2631 MapNode n = getNodeNoEx(p);
2632 if(n.getContent() != CONTENT_IGNORE)
2637 // If this node is not air, go to plan b
2638 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2640 // Search existing walkable and return it
2641 for(; p.Y>min; p.Y--)
2643 MapNode n = getNodeNoEx(p);
2644 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2653 Determine from map generator noise functions
2656 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2659 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2660 //return (s16)level;
2663 void ServerMap::createDatabase() {
2666 e = sqlite3_exec(m_database,
2667 "CREATE TABLE IF NOT EXISTS `blocks` ("
2668 "`pos` INT NOT NULL PRIMARY KEY,"
2671 , NULL, NULL, NULL);
2672 if(e == SQLITE_ABORT)
2673 throw FileNotGoodException("Could not create database structure");
2675 infostream<<"ServerMap: Database structure was created";
2678 void ServerMap::verifyDatabase() {
2683 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2684 bool needs_create = false;
2688 Open the database connection
2691 createDirs(m_savedir);
2693 if(!fs::PathExists(dbp))
2694 needs_create = true;
2696 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2697 if(d != SQLITE_OK) {
2698 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2699 throw FileNotGoodException("Cannot open database file");
2705 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2706 if(d != SQLITE_OK) {
2707 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2708 throw FileNotGoodException("Cannot prepare read statement");
2711 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2712 if(d != SQLITE_OK) {
2713 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2714 throw FileNotGoodException("Cannot prepare write statement");
2717 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2718 if(d != SQLITE_OK) {
2719 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2720 throw FileNotGoodException("Cannot prepare read statement");
2723 infostream<<"ServerMap: Database opened"<<std::endl;
2727 bool ServerMap::loadFromFolders() {
2728 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2733 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2734 return (sqlite3_int64)pos.Z*16777216 +
2735 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2738 void ServerMap::createDirs(std::string path)
2740 if(fs::CreateAllDirs(path) == false)
2742 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2743 <<"\""<<path<<"\""<<std::endl;
2744 throw BaseException("ServerMap failed to create directory");
2748 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2754 snprintf(cc, 9, "%.4x%.4x",
2755 (unsigned int)pos.X&0xffff,
2756 (unsigned int)pos.Y&0xffff);
2758 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2760 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2761 (unsigned int)pos.X&0xfff,
2762 (unsigned int)pos.Y&0xfff);
2764 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2770 v2s16 ServerMap::getSectorPos(std::string dirname)
2774 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2775 assert(spos != std::string::npos);
2776 if(dirname.size() - spos == 8)
2779 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2781 else if(dirname.size() - spos == 3)
2784 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2785 // Sign-extend the 12 bit values up to 16 bits...
2786 if(x&0x800) x|=0xF000;
2787 if(y&0x800) y|=0xF000;
2794 v2s16 pos((s16)x, (s16)y);
2798 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2800 v2s16 p2d = getSectorPos(sectordir);
2802 if(blockfile.size() != 4){
2803 throw InvalidFilenameException("Invalid block filename");
2806 int r = sscanf(blockfile.c_str(), "%4x", &y);
2808 throw InvalidFilenameException("Invalid block filename");
2809 return v3s16(p2d.X, y, p2d.Y);
2812 std::string ServerMap::getBlockFilename(v3s16 p)
2815 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2819 void ServerMap::save(ModifiedState save_level)
2821 DSTACK(__FUNCTION_NAME);
2822 if(m_map_saving_enabled == false)
2824 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2828 if(save_level == MOD_STATE_CLEAN)
2829 infostream<<"ServerMap: Saving whole map, this can take time."
2832 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2837 // Profile modified reasons
2838 Profiler modprofiler;
2840 u32 sector_meta_count = 0;
2841 u32 block_count = 0;
2842 u32 block_count_all = 0; // Number of blocks in memory
2844 // Don't do anything with sqlite unless something is really saved
2845 bool save_started = false;
2847 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2848 for(; i.atEnd() == false; i++)
2850 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2851 assert(sector->getId() == MAPSECTOR_SERVER);
2853 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2855 saveSectorMeta(sector);
2856 sector_meta_count++;
2858 core::list<MapBlock*> blocks;
2859 sector->getBlocks(blocks);
2860 core::list<MapBlock*>::Iterator j;
2862 for(j=blocks.begin(); j!=blocks.end(); j++)
2864 MapBlock *block = *j;
2868 if(block->getModified() >= save_level)
2873 save_started = true;
2876 modprofiler.add(block->getModifiedReason(), 1);
2881 /*infostream<<"ServerMap: Written block ("
2882 <<block->getPos().X<<","
2883 <<block->getPos().Y<<","
2884 <<block->getPos().Z<<")"
2893 Only print if something happened or saved whole map
2895 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2896 || block_count != 0)
2898 infostream<<"ServerMap: Written: "
2899 <<sector_meta_count<<" sector metadata files, "
2900 <<block_count<<" block files"
2901 <<", "<<block_count_all<<" blocks in memory."
2903 PrintInfo(infostream); // ServerMap/ClientMap:
2904 infostream<<"Blocks modified by: "<<std::endl;
2905 modprofiler.print(infostream);
2909 static s32 unsignedToSigned(s32 i, s32 max_positive)
2911 if(i < max_positive)
2914 return i - 2*max_positive;
2917 // modulo of a negative number does not work consistently in C
2918 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2922 return mod - ((-i) % mod);
2925 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2927 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2929 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2931 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2932 return v3s16(x,y,z);
2935 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2937 if(loadFromFolders()){
2938 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2939 <<"all blocks that are stored in flat files"<<std::endl;
2945 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2947 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2948 v3s16 p = getIntegerAsBlock(block_i);
2949 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2955 void ServerMap::saveMapMeta()
2957 DSTACK(__FUNCTION_NAME);
2959 /*infostream<<"ServerMap::saveMapMeta(): "
2963 createDirs(m_savedir);
2965 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2966 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2967 if(os.good() == false)
2969 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2970 <<"could not open"<<fullpath<<std::endl;
2971 throw FileNotGoodException("Cannot open chunk metadata");
2975 params.setU64("seed", m_seed);
2977 params.writeLines(os);
2979 os<<"[end_of_params]\n";
2981 m_map_metadata_changed = false;
2984 void ServerMap::loadMapMeta()
2986 DSTACK(__FUNCTION_NAME);
2988 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2991 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2992 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2993 if(is.good() == false)
2995 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2996 <<"could not open"<<fullpath<<std::endl;
2997 throw FileNotGoodException("Cannot open map metadata");
3005 throw SerializationError
3006 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3008 std::getline(is, line);
3009 std::string trimmedline = trim(line);
3010 if(trimmedline == "[end_of_params]")
3012 params.parseConfigLine(line);
3015 m_seed = params.getU64("seed");
3017 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3020 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3022 DSTACK(__FUNCTION_NAME);
3023 // Format used for writing
3024 u8 version = SER_FMT_VER_HIGHEST;
3026 v2s16 pos = sector->getPos();
3027 std::string dir = getSectorDir(pos);
3030 std::string fullpath = dir + DIR_DELIM + "meta";
3031 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3032 if(o.good() == false)
3033 throw FileNotGoodException("Cannot open sector metafile");
3035 sector->serialize(o, version);
3037 sector->differs_from_disk = false;
3040 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3042 DSTACK(__FUNCTION_NAME);
3044 v2s16 p2d = getSectorPos(sectordir);
3046 ServerMapSector *sector = NULL;
3048 std::string fullpath = sectordir + DIR_DELIM + "meta";
3049 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3050 if(is.good() == false)
3052 // If the directory exists anyway, it probably is in some old
3053 // format. Just go ahead and create the sector.
3054 if(fs::PathExists(sectordir))
3056 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3057 <<fullpath<<" doesn't exist but directory does."
3058 <<" Continuing with a sector with no metadata."
3060 sector = new ServerMapSector(this, p2d, m_gamedef);
3061 m_sectors.insert(p2d, sector);
3065 throw FileNotGoodException("Cannot open sector metafile");
3070 sector = ServerMapSector::deSerialize
3071 (is, this, p2d, m_sectors, m_gamedef);
3073 saveSectorMeta(sector);
3076 sector->differs_from_disk = false;
3081 bool ServerMap::loadSectorMeta(v2s16 p2d)
3083 DSTACK(__FUNCTION_NAME);
3085 MapSector *sector = NULL;
3087 // The directory layout we're going to load from.
3088 // 1 - original sectors/xxxxzzzz/
3089 // 2 - new sectors2/xxx/zzz/
3090 // If we load from anything but the latest structure, we will
3091 // immediately save to the new one, and remove the old.
3093 std::string sectordir1 = getSectorDir(p2d, 1);
3094 std::string sectordir;
3095 if(fs::PathExists(sectordir1))
3097 sectordir = sectordir1;
3102 sectordir = getSectorDir(p2d, 2);
3106 sector = loadSectorMeta(sectordir, loadlayout != 2);
3108 catch(InvalidFilenameException &e)
3112 catch(FileNotGoodException &e)
3116 catch(std::exception &e)
3125 bool ServerMap::loadSectorFull(v2s16 p2d)
3127 DSTACK(__FUNCTION_NAME);
3129 MapSector *sector = NULL;
3131 // The directory layout we're going to load from.
3132 // 1 - original sectors/xxxxzzzz/
3133 // 2 - new sectors2/xxx/zzz/
3134 // If we load from anything but the latest structure, we will
3135 // immediately save to the new one, and remove the old.
3137 std::string sectordir1 = getSectorDir(p2d, 1);
3138 std::string sectordir;
3139 if(fs::PathExists(sectordir1))
3141 sectordir = sectordir1;
3146 sectordir = getSectorDir(p2d, 2);
3150 sector = loadSectorMeta(sectordir, loadlayout != 2);
3152 catch(InvalidFilenameException &e)
3156 catch(FileNotGoodException &e)
3160 catch(std::exception &e)
3168 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3170 std::vector<fs::DirListNode>::iterator i2;
3171 for(i2=list2.begin(); i2!=list2.end(); i2++)
3177 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3179 catch(InvalidFilenameException &e)
3181 // This catches unknown crap in directory
3187 infostream<<"Sector converted to new layout - deleting "<<
3188 sectordir1<<std::endl;
3189 fs::RecursiveDelete(sectordir1);
3196 void ServerMap::beginSave() {
3198 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3199 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3202 void ServerMap::endSave() {
3204 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3205 infostream<<"WARNING: endSave() failed, map might not have saved.";
3208 void ServerMap::saveBlock(MapBlock *block)
3210 DSTACK(__FUNCTION_NAME);
3212 Dummy blocks are not written
3214 if(block->isDummy())
3216 /*v3s16 p = block->getPos();
3217 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3218 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3222 // Format used for writing
3223 u8 version = SER_FMT_VER_HIGHEST;
3225 v3s16 p3d = block->getPos();
3229 v2s16 p2d(p3d.X, p3d.Z);
3230 std::string sectordir = getSectorDir(p2d);
3232 createDirs(sectordir);
3234 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3235 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3236 if(o.good() == false)
3237 throw FileNotGoodException("Cannot open block data");
3240 [0] u8 serialization version
3246 std::ostringstream o(std::ios_base::binary);
3248 o.write((char*)&version, 1);
3251 block->serialize(o, version, true);
3253 // Write block to database
3255 std::string tmp = o.str();
3256 const char *bytes = tmp.c_str();
3258 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3259 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3260 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3261 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3262 int written = sqlite3_step(m_database_write);
3263 if(written != SQLITE_DONE)
3264 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3265 <<sqlite3_errmsg(m_database)<<std::endl;
3266 // Make ready for later reuse
3267 sqlite3_reset(m_database_write);
3269 // We just wrote it to the disk so clear modified flag
3270 block->resetModified();
3273 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3275 DSTACK(__FUNCTION_NAME);
3277 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3280 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3281 if(is.good() == false)
3282 throw FileNotGoodException("Cannot open block file");
3284 v3s16 p3d = getBlockPos(sectordir, blockfile);
3285 v2s16 p2d(p3d.X, p3d.Z);
3287 assert(sector->getPos() == p2d);
3289 u8 version = SER_FMT_VER_INVALID;
3290 is.read((char*)&version, 1);
3293 throw SerializationError("ServerMap::loadBlock(): Failed"
3294 " to read MapBlock version");
3296 /*u32 block_size = MapBlock::serializedLength(version);
3297 SharedBuffer<u8> data(block_size);
3298 is.read((char*)*data, block_size);*/
3300 // This will always return a sector because we're the server
3301 //MapSector *sector = emergeSector(p2d);
3303 MapBlock *block = NULL;
3304 bool created_new = false;
3305 block = sector->getBlockNoCreateNoEx(p3d.Y);
3308 block = sector->createBlankBlockNoInsert(p3d.Y);
3313 block->deSerialize(is, version, true);
3315 // If it's a new block, insert it to the map
3317 sector->insertBlock(block);
3320 Save blocks loaded in old format in new format
3323 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3327 // Should be in database now, so delete the old file
3328 fs::RecursiveDelete(fullpath);
3331 // We just loaded it from the disk, so it's up-to-date.
3332 block->resetModified();
3335 catch(SerializationError &e)
3337 infostream<<"WARNING: Invalid block data on disk "
3338 <<"fullpath="<<fullpath
3339 <<" (SerializationError). "
3340 <<"what()="<<e.what()
3342 //" Ignoring. A new one will be generated.
3345 // TODO: Backup file; name is in fullpath.
3349 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3351 DSTACK(__FUNCTION_NAME);
3354 std::istringstream is(*blob, std::ios_base::binary);
3356 u8 version = SER_FMT_VER_INVALID;
3357 is.read((char*)&version, 1);
3360 throw SerializationError("ServerMap::loadBlock(): Failed"
3361 " to read MapBlock version");
3363 /*u32 block_size = MapBlock::serializedLength(version);
3364 SharedBuffer<u8> data(block_size);
3365 is.read((char*)*data, block_size);*/
3367 // This will always return a sector because we're the server
3368 //MapSector *sector = emergeSector(p2d);
3370 MapBlock *block = NULL;
3371 bool created_new = false;
3372 block = sector->getBlockNoCreateNoEx(p3d.Y);
3375 block = sector->createBlankBlockNoInsert(p3d.Y);
3380 block->deSerialize(is, version, true);
3382 // If it's a new block, insert it to the map
3384 sector->insertBlock(block);
3387 Save blocks loaded in old format in new format
3390 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3391 // Only save if asked to; no need to update version
3395 // We just loaded it from, so it's up-to-date.
3396 block->resetModified();
3399 catch(SerializationError &e)
3401 infostream<<"WARNING: Invalid block data in database "
3402 <<" (SerializationError). "
3403 <<"what()="<<e.what()
3405 //" Ignoring. A new one will be generated.
3408 // TODO: Copy to a backup database.
3412 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3414 DSTACK(__FUNCTION_NAME);
3416 v2s16 p2d(blockpos.X, blockpos.Z);
3418 if(!loadFromFolders()) {
3421 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3422 infostream<<"WARNING: Could not bind block position for load: "
3423 <<sqlite3_errmsg(m_database)<<std::endl;
3424 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3426 Make sure sector is loaded
3428 MapSector *sector = createSector(p2d);
3433 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3434 size_t len = sqlite3_column_bytes(m_database_read, 0);
3436 std::string datastr(data, len);
3438 loadBlock(&datastr, blockpos, sector, false);
3440 sqlite3_step(m_database_read);
3441 // We should never get more than 1 row, so ok to reset
3442 sqlite3_reset(m_database_read);
3444 return getBlockNoCreateNoEx(blockpos);
3446 sqlite3_reset(m_database_read);
3448 // Not found in database, try the files
3451 // The directory layout we're going to load from.
3452 // 1 - original sectors/xxxxzzzz/
3453 // 2 - new sectors2/xxx/zzz/
3454 // If we load from anything but the latest structure, we will
3455 // immediately save to the new one, and remove the old.
3457 std::string sectordir1 = getSectorDir(p2d, 1);
3458 std::string sectordir;
3459 if(fs::PathExists(sectordir1))
3461 sectordir = sectordir1;
3466 sectordir = getSectorDir(p2d, 2);
3470 Make sure sector is loaded
3472 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3476 sector = loadSectorMeta(sectordir, loadlayout != 2);
3478 catch(InvalidFilenameException &e)
3482 catch(FileNotGoodException &e)
3486 catch(std::exception &e)
3493 Make sure file exists
3496 std::string blockfilename = getBlockFilename(blockpos);
3497 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3501 Load block and save it to the database
3503 loadBlock(sectordir, blockfilename, sector, true);
3504 return getBlockNoCreateNoEx(blockpos);
3507 void ServerMap::PrintInfo(std::ostream &out)
3516 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3521 MapVoxelManipulator::~MapVoxelManipulator()
3523 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3527 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3529 TimeTaker timer1("emerge", &emerge_time);
3531 // Units of these are MapBlocks
3532 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3533 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3535 VoxelArea block_area_nodes
3536 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3538 addArea(block_area_nodes);
3540 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3541 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3542 for(s32 x=p_min.X; x<=p_max.X; x++)
3545 core::map<v3s16, bool>::Node *n;
3546 n = m_loaded_blocks.find(p);
3550 bool block_data_inexistent = false;
3553 TimeTaker timer1("emerge load", &emerge_load_time);
3555 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3556 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3558 a.print(infostream);
3559 infostream<<std::endl;*/
3561 MapBlock *block = m_map->getBlockNoCreate(p);
3562 if(block->isDummy())
3563 block_data_inexistent = true;
3565 block->copyTo(*this);
3567 catch(InvalidPositionException &e)
3569 block_data_inexistent = true;
3572 if(block_data_inexistent)
3574 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3575 // Fill with VOXELFLAG_INEXISTENT
3576 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3577 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3579 s32 i = m_area.index(a.MinEdge.X,y,z);
3580 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3584 m_loaded_blocks.insert(p, !block_data_inexistent);
3587 //infostream<<"emerge done"<<std::endl;
3591 SUGG: Add an option to only update eg. water and air nodes.
3592 This will make it interfere less with important stuff if
3595 void MapVoxelManipulator::blitBack
3596 (core::map<v3s16, MapBlock*> & modified_blocks)
3598 if(m_area.getExtent() == v3s16(0,0,0))
3601 //TimeTaker timer1("blitBack");
3603 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3604 <<m_loaded_blocks.size()<<std::endl;*/
3607 Initialize block cache
3609 v3s16 blockpos_last;
3610 MapBlock *block = NULL;
3611 bool block_checked_in_modified = false;
3613 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3614 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3615 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3619 u8 f = m_flags[m_area.index(p)];
3620 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3623 MapNode &n = m_data[m_area.index(p)];
3625 v3s16 blockpos = getNodeBlockPos(p);
3630 if(block == NULL || blockpos != blockpos_last){
3631 block = m_map->getBlockNoCreate(blockpos);
3632 blockpos_last = blockpos;
3633 block_checked_in_modified = false;
3636 // Calculate relative position in block
3637 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3639 // Don't continue if nothing has changed here
3640 if(block->getNode(relpos) == n)
3643 //m_map->setNode(m_area.MinEdge + p, n);
3644 block->setNode(relpos, n);
3647 Make sure block is in modified_blocks
3649 if(block_checked_in_modified == false)
3651 modified_blocks[blockpos] = block;
3652 block_checked_in_modified = true;
3655 catch(InvalidPositionException &e)
3661 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3662 MapVoxelManipulator(map),
3663 m_create_area(false)
3667 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3671 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3673 // Just create the area so that it can be pointed to
3674 VoxelManipulator::emerge(a, caller_id);
3677 void ManualMapVoxelManipulator::initialEmerge(
3678 v3s16 blockpos_min, v3s16 blockpos_max)
3680 TimeTaker timer1("initialEmerge", &emerge_time);
3682 // Units of these are MapBlocks
3683 v3s16 p_min = blockpos_min;
3684 v3s16 p_max = blockpos_max;
3686 VoxelArea block_area_nodes
3687 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3689 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3692 infostream<<"initialEmerge: area: ";
3693 block_area_nodes.print(infostream);
3694 infostream<<" ("<<size_MB<<"MB)";
3695 infostream<<std::endl;
3698 addArea(block_area_nodes);
3700 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3701 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3702 for(s32 x=p_min.X; x<=p_max.X; x++)
3705 core::map<v3s16, bool>::Node *n;
3706 n = m_loaded_blocks.find(p);
3710 bool block_data_inexistent = false;
3713 TimeTaker timer1("emerge load", &emerge_load_time);
3715 MapBlock *block = m_map->getBlockNoCreate(p);
3716 if(block->isDummy())
3717 block_data_inexistent = true;
3719 block->copyTo(*this);
3721 catch(InvalidPositionException &e)
3723 block_data_inexistent = true;
3726 if(block_data_inexistent)
3729 Mark area inexistent
3731 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3732 // Fill with VOXELFLAG_INEXISTENT
3733 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3734 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3736 s32 i = m_area.index(a.MinEdge.X,y,z);
3737 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3741 m_loaded_blocks.insert(p, !block_data_inexistent);
3745 void ManualMapVoxelManipulator::blitBackAll(
3746 core::map<v3s16, MapBlock*> * modified_blocks)
3748 if(m_area.getExtent() == v3s16(0,0,0))
3752 Copy data of all blocks
3754 for(core::map<v3s16, bool>::Iterator
3755 i = m_loaded_blocks.getIterator();
3756 i.atEnd() == false; i++)
3758 v3s16 p = i.getNode()->getKey();
3759 bool existed = i.getNode()->getValue();
3760 if(existed == false)
3762 // The Great Bug was found using this
3763 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3764 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3768 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3771 infostream<<"WARNING: "<<__FUNCTION_NAME
3772 <<": got NULL block "
3773 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3778 block->copyFrom(*this);
3781 modified_blocks->insert(p, block);