3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
29 #include "nodemetadata.h"
36 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
42 If map.sqlite does not exist in the save dir
43 or the block was not found in the database
44 the map will try to load from sectors folder.
45 In either case, map.sqlite will be created
46 and all future saves will save there.
48 Structure of map.sqlite:
59 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 /*m_sector_mutex.Init();
65 assert(m_sector_mutex.IsInitialized());*/
73 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
74 for(; i.atEnd() == false; i++)
76 MapSector *sector = i.getNode()->getValue();
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver, false);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 if(m_event_receivers.find(event_receiver) == NULL)
90 m_event_receivers.remove(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(core::map<MapEventReceiver*, bool>::Iterator
96 i = m_event_receivers.getIterator();
97 i.atEnd()==false; i++)
99 MapEventReceiver* event_receiver = i.getNode()->getKey();
100 event_receiver->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
116 MapSector *sector = n->getValue();
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 core::map<v3s16, u8> & from_nodes,
237 core::map<v3s16, bool> & light_sources,
238 core::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 core::map<v3s16, u8> unlighted_nodes;
257 core::map<v3s16, u8>::Iterator j;
258 j = from_nodes.getIterator();
261 Initialize block cache
264 MapBlock *block = NULL;
265 // Cache this a bit, too
266 bool block_checked_in_modified = false;
268 for(; j.atEnd() == false; j++)
270 v3s16 pos = j.getNode()->getKey();
271 v3s16 blockpos = getNodeBlockPos(pos);
273 // Only fetch a new block if the block position has changed
275 if(block == NULL || blockpos != blockpos_last){
276 block = getBlockNoCreate(blockpos);
277 blockpos_last = blockpos;
279 block_checked_in_modified = false;
283 catch(InvalidPositionException &e)
291 // Calculate relative position in block
292 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
294 // Get node straight from the block
295 MapNode n = block->getNode(relpos);
297 u8 oldlight = j.getNode()->getValue();
299 // Loop through 6 neighbors
300 for(u16 i=0; i<6; i++)
302 // Get the position of the neighbor node
303 v3s16 n2pos = pos + dirs[i];
305 // Get the block where the node is located
306 v3s16 blockpos = getNodeBlockPos(n2pos);
310 // Only fetch a new block if the block position has changed
312 if(block == NULL || blockpos != blockpos_last){
313 block = getBlockNoCreate(blockpos);
314 blockpos_last = blockpos;
316 block_checked_in_modified = false;
320 catch(InvalidPositionException &e)
325 // Calculate relative position in block
326 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
327 // Get node straight from the block
328 MapNode n2 = block->getNode(relpos);
330 bool changed = false;
332 //TODO: Optimize output by optimizing light_sources?
335 If the neighbor is dimmer than what was specified
336 as oldlight (the light of the previous node)
338 if(n2.getLight(bank, nodemgr) < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates
344 && n2.getLight(bank, nodemgr) != 0)
347 Set light to 0 and add to queue
350 u8 current_light = n2.getLight(bank, nodemgr);
351 n2.setLight(bank, 0, nodemgr);
352 block->setNode(relpos, n2);
354 unlighted_nodes.insert(n2pos, current_light);
358 Remove from light_sources if it is there
359 NOTE: This doesn't happen nearly at all
361 /*if(light_sources.find(n2pos))
363 infostream<<"Removed from light_sources"<<std::endl;
364 light_sources.remove(n2pos);
369 if(light_sources.find(n2pos) != NULL)
370 light_sources.remove(n2pos);*/
373 light_sources.insert(n2pos, true);
376 // Add to modified_blocks
377 if(changed == true && block_checked_in_modified == false)
379 // If the block is not found in modified_blocks, add.
380 if(modified_blocks.find(blockpos) == NULL)
382 modified_blocks.insert(blockpos, block);
384 block_checked_in_modified = true;
387 catch(InvalidPositionException &e)
394 /*infostream<<"unspreadLight(): Changed block "
395 <<blockchangecount<<" times"
396 <<" for "<<from_nodes.size()<<" nodes"
399 if(unlighted_nodes.size() > 0)
400 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
404 A single-node wrapper of the above
406 void Map::unLightNeighbors(enum LightBank bank,
407 v3s16 pos, u8 lightwas,
408 core::map<v3s16, bool> & light_sources,
409 core::map<v3s16, MapBlock*> & modified_blocks)
411 core::map<v3s16, u8> from_nodes;
412 from_nodes.insert(pos, lightwas);
414 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 core::map<v3s16, bool> & from_nodes,
423 core::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.size() == 0)
439 u32 blockchangecount = 0;
441 core::map<v3s16, bool> lighted_nodes;
442 core::map<v3s16, bool>::Iterator j;
443 j = from_nodes.getIterator();
446 Initialize block cache
449 MapBlock *block = NULL;
450 // Cache this a bit, too
451 bool block_checked_in_modified = false;
453 for(; j.atEnd() == false; j++)
454 //for(; j != from_nodes.end(); j++)
456 v3s16 pos = j.getNode()->getKey();
458 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
459 v3s16 blockpos = getNodeBlockPos(pos);
461 // Only fetch a new block if the block position has changed
463 if(block == NULL || blockpos != blockpos_last){
464 block = getBlockNoCreate(blockpos);
465 blockpos_last = blockpos;
467 block_checked_in_modified = false;
471 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
482 // Get node straight from the block
483 MapNode n = block->getNode(relpos);
485 u8 oldlight = n.getLight(bank, nodemgr);
486 u8 newlight = diminish_light(oldlight);
488 // Loop through 6 neighbors
489 for(u16 i=0; i<6; i++){
490 // Get the position of the neighbor node
491 v3s16 n2pos = pos + dirs[i];
493 // Get the block where the node is located
494 v3s16 blockpos = getNodeBlockPos(n2pos);
498 // Only fetch a new block if the block position has changed
500 if(block == NULL || blockpos != blockpos_last){
501 block = getBlockNoCreate(blockpos);
502 blockpos_last = blockpos;
504 block_checked_in_modified = false;
508 catch(InvalidPositionException &e)
513 // Calculate relative position in block
514 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos);
518 bool changed = false;
520 If the neighbor is brighter than the current node,
521 add to list (it will light up this node on its turn)
523 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
525 lighted_nodes.insert(n2pos, true);
526 //lighted_nodes.push_back(n2pos);
530 If the neighbor is dimmer than how much light this node
531 would spread on it, add to list
533 if(n2.getLight(bank, nodemgr) < newlight)
535 if(nodemgr->get(n2).light_propagates)
537 n2.setLight(bank, newlight, nodemgr);
538 block->setNode(relpos, n2);
539 lighted_nodes.insert(n2pos, true);
540 //lighted_nodes.push_back(n2pos);
545 // Add to modified_blocks
546 if(changed == true && block_checked_in_modified == false)
548 // If the block is not found in modified_blocks, add.
549 if(modified_blocks.find(blockpos) == NULL)
551 modified_blocks.insert(blockpos, block);
553 block_checked_in_modified = true;
556 catch(InvalidPositionException &e)
563 /*infostream<<"spreadLight(): Changed block "
564 <<blockchangecount<<" times"
565 <<" for "<<from_nodes.size()<<" nodes"
568 if(lighted_nodes.size() > 0)
569 spreadLight(bank, lighted_nodes, modified_blocks);
573 A single-node source variation of the above.
575 void Map::lightNeighbors(enum LightBank bank,
577 core::map<v3s16, MapBlock*> & modified_blocks)
579 core::map<v3s16, bool> from_nodes;
580 from_nodes.insert(pos, true);
581 spreadLight(bank, from_nodes, modified_blocks);
584 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
586 INodeDefManager *nodemgr = m_gamedef->ndef();
589 v3s16(0,0,1), // back
591 v3s16(1,0,0), // right
592 v3s16(0,0,-1), // front
593 v3s16(0,-1,0), // bottom
594 v3s16(-1,0,0), // left
597 u8 brightest_light = 0;
598 v3s16 brightest_pos(0,0,0);
599 bool found_something = false;
601 // Loop through 6 neighbors
602 for(u16 i=0; i<6; i++){
603 // Get the position of the neighbor node
604 v3s16 n2pos = p + dirs[i];
609 catch(InvalidPositionException &e)
613 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
614 brightest_light = n2.getLight(bank, nodemgr);
615 brightest_pos = n2pos;
616 found_something = true;
620 if(found_something == false)
621 throw InvalidPositionException();
623 return brightest_pos;
627 Propagates sunlight down from a node.
628 Starting point gets sunlight.
630 Returns the lowest y value of where the sunlight went.
632 Mud is turned into grass in where the sunlight stops.
634 s16 Map::propagateSunlight(v3s16 start,
635 core::map<v3s16, MapBlock*> & modified_blocks)
637 INodeDefManager *nodemgr = m_gamedef->ndef();
642 v3s16 pos(start.X, y, start.Z);
644 v3s16 blockpos = getNodeBlockPos(pos);
647 block = getBlockNoCreate(blockpos);
649 catch(InvalidPositionException &e)
654 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
655 MapNode n = block->getNode(relpos);
657 if(nodemgr->get(n).sunlight_propagates)
659 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660 block->setNode(relpos, n);
662 modified_blocks.insert(blockpos, block);
666 // Sunlight goes no further
673 void Map::updateLighting(enum LightBank bank,
674 core::map<v3s16, MapBlock*> & a_blocks,
675 core::map<v3s16, MapBlock*> & modified_blocks)
677 INodeDefManager *nodemgr = m_gamedef->ndef();
679 /*m_dout<<DTIME<<"Map::updateLighting(): "
680 <<a_blocks.size()<<" blocks."<<std::endl;*/
682 //TimeTaker timer("updateLighting");
686 //u32 count_was = modified_blocks.size();
688 core::map<v3s16, MapBlock*> blocks_to_update;
690 core::map<v3s16, bool> light_sources;
692 core::map<v3s16, u8> unlight_from;
694 int num_bottom_invalid = 0;
697 //TimeTaker t("first stuff");
699 core::map<v3s16, MapBlock*>::Iterator i;
700 i = a_blocks.getIterator();
701 for(; i.atEnd() == false; i++)
703 MapBlock *block = i.getNode()->getValue();
707 // Don't bother with dummy blocks.
711 v3s16 pos = block->getPos();
712 v3s16 posnodes = block->getPosRelative();
713 modified_blocks.insert(pos, block);
715 blocks_to_update.insert(pos, block);
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(p);
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(p, n);
732 // If node sources light, add to list
733 u8 source = nodemgr->get(n).light_source;
735 light_sources[p + posnodes] = true;
737 // Collect borders for unlighting
738 if((x==0 || x == MAP_BLOCKSIZE-1
739 || y==0 || y == MAP_BLOCKSIZE-1
740 || z==0 || z == MAP_BLOCKSIZE-1)
743 v3s16 p_map = p + posnodes;
744 unlight_from.insert(p_map, oldlight);
747 catch(InvalidPositionException &e)
750 This would happen when dealing with a
754 infostream<<"updateLighting(): InvalidPositionException"
759 if(bank == LIGHTBANK_DAY)
761 bool bottom_valid = block->propagateSunlight(light_sources);
764 num_bottom_invalid++;
766 // If bottom is valid, we're done.
770 else if(bank == LIGHTBANK_NIGHT)
772 // For night lighting, sunlight is not propagated
777 // Invalid lighting bank
781 /*infostream<<"Bottom for sunlight-propagated block ("
782 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
785 // Bottom sunlight is not valid; get the block and loop to it
789 block = getBlockNoCreate(pos);
791 catch(InvalidPositionException &e)
802 Enable this to disable proper lighting for speeding up map
803 generation for testing or whatever
806 //if(g_settings->get(""))
808 core::map<v3s16, MapBlock*>::Iterator i;
809 i = blocks_to_update.getIterator();
810 for(; i.atEnd() == false; i++)
812 MapBlock *block = i.getNode()->getValue();
813 v3s16 p = block->getPos();
814 block->setLightingExpired(false);
822 //TimeTaker timer("unspreadLight");
823 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
828 u32 diff = modified_blocks.size() - count_was;
829 count_was = modified_blocks.size();
830 infostream<<"unspreadLight modified "<<diff<<std::endl;
834 //TimeTaker timer("spreadLight");
835 spreadLight(bank, light_sources, modified_blocks);
840 u32 diff = modified_blocks.size() - count_was;
841 count_was = modified_blocks.size();
842 infostream<<"spreadLight modified "<<diff<<std::endl;
848 //MapVoxelManipulator vmanip(this);
850 // Make a manual voxel manipulator and load all the blocks
851 // that touch the requested blocks
852 ManualMapVoxelManipulator vmanip(this);
855 //TimeTaker timer("initialEmerge");
857 core::map<v3s16, MapBlock*>::Iterator i;
858 i = blocks_to_update.getIterator();
859 for(; i.atEnd() == false; i++)
861 MapBlock *block = i.getNode()->getValue();
862 v3s16 p = block->getPos();
864 // Add all surrounding blocks
865 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
868 Add all surrounding blocks that have up-to-date lighting
869 NOTE: This doesn't quite do the job (not everything
870 appropriate is lighted)
872 /*for(s16 z=-1; z<=1; z++)
873 for(s16 y=-1; y<=1; y++)
874 for(s16 x=-1; x<=1; x++)
876 v3s16 p2 = p + v3s16(x,y,z);
877 MapBlock *block = getBlockNoCreateNoEx(p2);
882 if(block->getLightingExpired())
884 vmanip.initialEmerge(p2, p2);
887 // Lighting of block will be updated completely
888 block->setLightingExpired(false);
893 //TimeTaker timer("unSpreadLight");
894 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
897 //TimeTaker timer("spreadLight");
898 vmanip.spreadLight(bank, light_sources, nodemgr);
901 //TimeTaker timer("blitBack");
902 vmanip.blitBack(modified_blocks);
904 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
909 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
912 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
913 core::map<v3s16, MapBlock*> & modified_blocks)
915 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
916 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
919 Update information about whether day and night light differ
921 for(core::map<v3s16, MapBlock*>::Iterator
922 i = modified_blocks.getIterator();
923 i.atEnd() == false; i++)
925 MapBlock *block = i.getNode()->getValue();
926 block->expireDayNightDiff();
932 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
933 core::map<v3s16, MapBlock*> &modified_blocks)
935 INodeDefManager *nodemgr = m_gamedef->ndef();
938 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 From this node to nodes underneath:
943 If lighting is sunlight (1.0), unlight neighbours and
948 v3s16 toppos = p + v3s16(0,1,0);
949 v3s16 bottompos = p + v3s16(0,-1,0);
951 bool node_under_sunlight = true;
952 core::map<v3s16, bool> light_sources;
955 If there is a node at top and it doesn't have sunlight,
956 there has not been any sunlight going down.
958 Otherwise there probably is.
961 MapNode topnode = getNode(toppos);
963 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
964 node_under_sunlight = false;
966 catch(InvalidPositionException &e)
971 Remove all light that has come out of this node
974 enum LightBank banks[] =
979 for(s32 i=0; i<2; i++)
981 enum LightBank bank = banks[i];
983 u8 lightwas = getNode(p).getLight(bank, nodemgr);
985 // Add the block of the added node to modified_blocks
986 v3s16 blockpos = getNodeBlockPos(p);
987 MapBlock * block = getBlockNoCreate(blockpos);
988 assert(block != NULL);
989 modified_blocks.insert(blockpos, block);
991 assert(isValidPosition(p));
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1000 n.setLight(bank, 0, nodemgr);
1004 If node lets sunlight through and is under sunlight, it has
1007 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1009 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1013 Set the node on the map
1022 std::string metadata_name = nodemgr->get(n).metadata_name;
1023 if(metadata_name != ""){
1024 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1026 errorstream<<"Failed to create node metadata \""
1027 <<metadata_name<<"\""<<std::endl;
1029 setNodeMetadata(p, meta);
1034 If node is under sunlight and doesn't let sunlight through,
1035 take all sunlighted nodes under it and clear light from them
1036 and from where the light has been spread.
1037 TODO: This could be optimized by mass-unlighting instead
1040 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1044 //m_dout<<DTIME<<"y="<<y<<std::endl;
1045 v3s16 n2pos(p.X, y, p.Z);
1049 n2 = getNode(n2pos);
1051 catch(InvalidPositionException &e)
1056 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1058 unLightNeighbors(LIGHTBANK_DAY,
1059 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1060 light_sources, modified_blocks);
1061 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Spread light from all nodes that might be capable of doing so
1076 spreadLight(bank, light_sources, modified_blocks);
1080 Update information about whether day and night light differ
1082 for(core::map<v3s16, MapBlock*>::Iterator
1083 i = modified_blocks.getIterator();
1084 i.atEnd() == false; i++)
1086 MapBlock *block = i.getNode()->getValue();
1087 block->expireDayNightDiff();
1091 Add neighboring liquid nodes and the node itself if it is
1092 liquid (=water node was added) to transform queue.
1095 v3s16(0,0,0), // self
1096 v3s16(0,0,1), // back
1097 v3s16(0,1,0), // top
1098 v3s16(1,0,0), // right
1099 v3s16(0,0,-1), // front
1100 v3s16(0,-1,0), // bottom
1101 v3s16(-1,0,0), // left
1103 for(u16 i=0; i<7; i++)
1108 v3s16 p2 = p + dirs[i];
1110 MapNode n2 = getNode(p2);
1111 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1113 m_transforming_liquid.push_back(p2);
1116 }catch(InvalidPositionException &e)
1124 void Map::removeNodeAndUpdate(v3s16 p,
1125 core::map<v3s16, MapBlock*> &modified_blocks)
1127 INodeDefManager *nodemgr = m_gamedef->ndef();
1129 /*PrintInfo(m_dout);
1130 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1131 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1133 bool node_under_sunlight = true;
1135 v3s16 toppos = p + v3s16(0,1,0);
1137 // Node will be replaced with this
1138 content_t replace_material = CONTENT_AIR;
1141 If there is a node at top and it doesn't have sunlight,
1142 there will be no sunlight going down.
1145 MapNode topnode = getNode(toppos);
1147 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1148 node_under_sunlight = false;
1150 catch(InvalidPositionException &e)
1154 core::map<v3s16, bool> light_sources;
1156 enum LightBank banks[] =
1161 for(s32 i=0; i<2; i++)
1163 enum LightBank bank = banks[i];
1166 Unlight neighbors (in case the node is a light source)
1168 unLightNeighbors(bank, p,
1169 getNode(p).getLight(bank, nodemgr),
1170 light_sources, modified_blocks);
1174 Remove node metadata
1177 removeNodeMetadata(p);
1181 This also clears the lighting.
1185 n.setContent(replace_material);
1188 for(s32 i=0; i<2; i++)
1190 enum LightBank bank = banks[i];
1193 Recalculate lighting
1195 spreadLight(bank, light_sources, modified_blocks);
1198 // Add the block of the removed node to modified_blocks
1199 v3s16 blockpos = getNodeBlockPos(p);
1200 MapBlock * block = getBlockNoCreate(blockpos);
1201 assert(block != NULL);
1202 modified_blocks.insert(blockpos, block);
1205 If the removed node was under sunlight, propagate the
1206 sunlight down from it and then light all neighbors
1207 of the propagated blocks.
1209 if(node_under_sunlight)
1211 s16 ybottom = propagateSunlight(p, modified_blocks);
1212 /*m_dout<<DTIME<<"Node was under sunlight. "
1213 "Propagating sunlight";
1214 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1216 for(; y >= ybottom; y--)
1218 v3s16 p2(p.X, y, p.Z);
1219 /*m_dout<<DTIME<<"lighting neighbors of node ("
1220 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1222 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1227 // Set the lighting of this node to 0
1228 // TODO: Is this needed? Lighting is cleared up there already.
1230 MapNode n = getNode(p);
1231 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1234 catch(InvalidPositionException &e)
1240 for(s32 i=0; i<2; i++)
1242 enum LightBank bank = banks[i];
1244 // Get the brightest neighbour node and propagate light from it
1245 v3s16 n2p = getBrightestNeighbour(bank, p);
1247 MapNode n2 = getNode(n2p);
1248 lightNeighbors(bank, n2p, modified_blocks);
1250 catch(InvalidPositionException &e)
1256 Update information about whether day and night light differ
1258 for(core::map<v3s16, MapBlock*>::Iterator
1259 i = modified_blocks.getIterator();
1260 i.atEnd() == false; i++)
1262 MapBlock *block = i.getNode()->getValue();
1263 block->expireDayNightDiff();
1267 Add neighboring liquid nodes and this node to transform queue.
1268 (it's vital for the node itself to get updated last.)
1271 v3s16(0,0,1), // back
1272 v3s16(0,1,0), // top
1273 v3s16(1,0,0), // right
1274 v3s16(0,0,-1), // front
1275 v3s16(0,-1,0), // bottom
1276 v3s16(-1,0,0), // left
1277 v3s16(0,0,0), // self
1279 for(u16 i=0; i<7; i++)
1284 v3s16 p2 = p + dirs[i];
1286 MapNode n2 = getNode(p2);
1287 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1289 m_transforming_liquid.push_back(p2);
1292 }catch(InvalidPositionException &e)
1298 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1300 ScopeProfiler sp(g_profiler, "Map::addNodeWithEvent", SPT_AVG);
1302 event.type = MEET_ADDNODE;
1306 bool succeeded = true;
1308 core::map<v3s16, MapBlock*> modified_blocks;
1309 addNodeAndUpdate(p, n, modified_blocks);
1311 // Copy modified_blocks to event
1312 for(core::map<v3s16, MapBlock*>::Iterator
1313 i = modified_blocks.getIterator();
1314 i.atEnd()==false; i++)
1316 event.modified_blocks.insert(i.getNode()->getKey(), false);
1319 catch(InvalidPositionException &e){
1323 dispatchEvent(&event);
1328 bool Map::removeNodeWithEvent(v3s16 p)
1331 event.type = MEET_REMOVENODE;
1334 bool succeeded = true;
1336 core::map<v3s16, MapBlock*> modified_blocks;
1337 removeNodeAndUpdate(p, modified_blocks);
1339 // Copy modified_blocks to event
1340 for(core::map<v3s16, MapBlock*>::Iterator
1341 i = modified_blocks.getIterator();
1342 i.atEnd()==false; i++)
1344 event.modified_blocks.insert(i.getNode()->getKey(), false);
1347 catch(InvalidPositionException &e){
1351 dispatchEvent(&event);
1356 bool Map::getDayNightDiff(v3s16 blockpos)
1359 v3s16 p = blockpos + v3s16(0,0,0);
1360 MapBlock *b = getBlockNoCreate(p);
1361 if(b->getDayNightDiff())
1364 catch(InvalidPositionException &e){}
1367 v3s16 p = blockpos + v3s16(-1,0,0);
1368 MapBlock *b = getBlockNoCreate(p);
1369 if(b->getDayNightDiff())
1372 catch(InvalidPositionException &e){}
1374 v3s16 p = blockpos + v3s16(0,-1,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->getDayNightDiff())
1379 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(0,0,-1);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->getDayNightDiff())
1386 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(1,0,0);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->getDayNightDiff())
1394 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(0,1,0);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->getDayNightDiff())
1401 catch(InvalidPositionException &e){}
1403 v3s16 p = blockpos + v3s16(0,0,1);
1404 MapBlock *b = getBlockNoCreate(p);
1405 if(b->getDayNightDiff())
1408 catch(InvalidPositionException &e){}
1414 Updates usage timers
1416 void Map::timerUpdate(float dtime, float unload_timeout,
1417 core::list<v3s16> *unloaded_blocks)
1419 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1421 // Profile modified reasons
1422 Profiler modprofiler;
1424 core::list<v2s16> sector_deletion_queue;
1425 u32 deleted_blocks_count = 0;
1426 u32 saved_blocks_count = 0;
1427 u32 block_count_all = 0;
1429 core::map<v2s16, MapSector*>::Iterator si;
1432 si = m_sectors.getIterator();
1433 for(; si.atEnd() == false; si++)
1435 MapSector *sector = si.getNode()->getValue();
1437 bool all_blocks_deleted = true;
1439 core::list<MapBlock*> blocks;
1440 sector->getBlocks(blocks);
1442 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1443 i != blocks.end(); i++)
1445 MapBlock *block = (*i);
1447 block->incrementUsageTimer(dtime);
1449 if(block->getUsageTimer() > unload_timeout)
1451 v3s16 p = block->getPos();
1454 if(block->getModified() != MOD_STATE_CLEAN
1455 && save_before_unloading)
1457 modprofiler.add(block->getModifiedReason(), 1);
1459 saved_blocks_count++;
1462 // Delete from memory
1463 sector->deleteBlock(block);
1466 unloaded_blocks->push_back(p);
1468 deleted_blocks_count++;
1472 all_blocks_deleted = false;
1477 if(all_blocks_deleted)
1479 sector_deletion_queue.push_back(si.getNode()->getKey());
1484 // Finally delete the empty sectors
1485 deleteSectors(sector_deletion_queue);
1487 if(deleted_blocks_count != 0)
1489 PrintInfo(infostream); // ServerMap/ClientMap:
1490 infostream<<"Unloaded "<<deleted_blocks_count
1491 <<" blocks from memory";
1492 if(save_before_unloading)
1493 infostream<<", of which "<<saved_blocks_count<<" were written";
1494 infostream<<", "<<block_count_all<<" blocks in memory";
1495 infostream<<"."<<std::endl;
1496 if(saved_blocks_count != 0){
1497 PrintInfo(infostream); // ServerMap/ClientMap:
1498 infostream<<"Blocks modified by: "<<std::endl;
1499 modprofiler.print(infostream);
1504 void Map::deleteSectors(core::list<v2s16> &list)
1506 core::list<v2s16>::Iterator j;
1507 for(j=list.begin(); j!=list.end(); j++)
1509 MapSector *sector = m_sectors[*j];
1510 // If sector is in sector cache, remove it from there
1511 if(m_sector_cache == sector)
1512 m_sector_cache = NULL;
1513 // Remove from map and delete
1514 m_sectors.remove(*j);
1520 void Map::unloadUnusedData(float timeout,
1521 core::list<v3s16> *deleted_blocks)
1523 core::list<v2s16> sector_deletion_queue;
1524 u32 deleted_blocks_count = 0;
1525 u32 saved_blocks_count = 0;
1527 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1528 for(; si.atEnd() == false; si++)
1530 MapSector *sector = si.getNode()->getValue();
1532 bool all_blocks_deleted = true;
1534 core::list<MapBlock*> blocks;
1535 sector->getBlocks(blocks);
1536 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1537 i != blocks.end(); i++)
1539 MapBlock *block = (*i);
1541 if(block->getUsageTimer() > timeout)
1544 if(block->getModified() != MOD_STATE_CLEAN)
1547 saved_blocks_count++;
1549 // Delete from memory
1550 sector->deleteBlock(block);
1551 deleted_blocks_count++;
1555 all_blocks_deleted = false;
1559 if(all_blocks_deleted)
1561 sector_deletion_queue.push_back(si.getNode()->getKey());
1565 deleteSectors(sector_deletion_queue);
1567 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1568 <<", of which "<<saved_blocks_count<<" were wr."
1571 //return sector_deletion_queue.getSize();
1572 //return deleted_blocks_count;
1576 void Map::PrintInfo(std::ostream &out)
1581 #define WATER_DROP_BOOST 4
1585 NEIGHBOR_SAME_LEVEL,
1588 struct NodeNeighbor {
1594 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1596 INodeDefManager *nodemgr = m_gamedef->ndef();
1598 DSTACK(__FUNCTION_NAME);
1599 //TimeTaker timer("transformLiquids()");
1602 u32 initial_size = m_transforming_liquid.size();
1604 /*if(initial_size != 0)
1605 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1607 // list of nodes that due to viscosity have not reached their max level height
1608 UniqueQueue<v3s16> must_reflow;
1610 // List of MapBlocks that will require a lighting update (due to lava)
1611 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1613 while(m_transforming_liquid.size() != 0)
1615 // This should be done here so that it is done when continue is used
1616 if(loopcount >= initial_size * 3)
1621 Get a queued transforming liquid node
1623 v3s16 p0 = m_transforming_liquid.pop_front();
1625 MapNode n0 = getNodeNoEx(p0);
1628 Collect information about current node
1630 s8 liquid_level = -1;
1631 u8 liquid_kind = CONTENT_IGNORE;
1632 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1633 switch (liquid_type) {
1635 liquid_level = LIQUID_LEVEL_SOURCE;
1636 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1638 case LIQUID_FLOWING:
1639 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1640 liquid_kind = n0.getContent();
1643 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1644 // continue with the next node.
1645 if (n0.getContent() != CONTENT_AIR)
1647 liquid_kind = CONTENT_AIR;
1652 Collect information about the environment
1654 const v3s16 *dirs = g_6dirs;
1655 NodeNeighbor sources[6]; // surrounding sources
1656 int num_sources = 0;
1657 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1659 NodeNeighbor airs[6]; // surrounding air
1661 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1662 int num_neutrals = 0;
1663 bool flowing_down = false;
1664 for (u16 i = 0; i < 6; i++) {
1665 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1668 nt = NEIGHBOR_UPPER;
1671 nt = NEIGHBOR_LOWER;
1674 v3s16 npos = p0 + dirs[i];
1675 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1676 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1678 if (nb.n.getContent() == CONTENT_AIR) {
1679 airs[num_airs++] = nb;
1680 // if the current node is a water source the neighbor
1681 // should be enqueded for transformation regardless of whether the
1682 // current node changes or not.
1683 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1684 m_transforming_liquid.push_back(npos);
1685 // if the current node happens to be a flowing node, it will start to flow down here.
1686 if (nb.t == NEIGHBOR_LOWER) {
1687 flowing_down = true;
1690 neutrals[num_neutrals++] = nb;
1694 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1695 if (liquid_kind == CONTENT_AIR)
1696 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1697 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1698 neutrals[num_neutrals++] = nb;
1700 // Do not count bottom source, it will screw things up
1702 sources[num_sources++] = nb;
1705 case LIQUID_FLOWING:
1706 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1707 if (liquid_kind == CONTENT_AIR)
1708 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1709 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1710 neutrals[num_neutrals++] = nb;
1712 flows[num_flows++] = nb;
1713 if (nb.t == NEIGHBOR_LOWER)
1714 flowing_down = true;
1721 decide on the type (and possibly level) of the current node
1723 content_t new_node_content;
1724 s8 new_node_level = -1;
1725 s8 max_node_level = -1;
1726 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1727 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1728 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1729 // it's perfectly safe to use liquid_kind here to determine the new node content.
1730 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1731 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1732 // liquid_kind is set properly, see above
1733 new_node_content = liquid_kind;
1734 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1736 // no surrounding sources, so get the maximum level that can flow into this node
1737 for (u16 i = 0; i < num_flows; i++) {
1738 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1739 switch (flows[i].t) {
1740 case NEIGHBOR_UPPER:
1741 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1742 max_node_level = LIQUID_LEVEL_MAX;
1743 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1744 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1745 } else if (nb_liquid_level > max_node_level)
1746 max_node_level = nb_liquid_level;
1748 case NEIGHBOR_LOWER:
1750 case NEIGHBOR_SAME_LEVEL:
1751 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1752 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1753 max_node_level = nb_liquid_level - 1;
1759 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1760 if (viscosity > 1 && max_node_level != liquid_level) {
1761 // amount to gain, limited by viscosity
1762 // must be at least 1 in absolute value
1763 s8 level_inc = max_node_level - liquid_level;
1764 if (level_inc < -viscosity || level_inc > viscosity)
1765 new_node_level = liquid_level + level_inc/viscosity;
1766 else if (level_inc < 0)
1767 new_node_level = liquid_level - 1;
1768 else if (level_inc > 0)
1769 new_node_level = liquid_level + 1;
1770 if (new_node_level != max_node_level)
1771 must_reflow.push_back(p0);
1773 new_node_level = max_node_level;
1775 if (new_node_level >= 0)
1776 new_node_content = liquid_kind;
1778 new_node_content = CONTENT_AIR;
1783 check if anything has changed. if not, just continue with the next node.
1785 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1786 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1787 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1793 update the current node
1795 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1796 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1797 // set level to last 3 bits, flowing down bit to 4th bit
1798 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1800 // set the liquid level and flow bit to 0
1801 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1803 n0.setContent(new_node_content);
1805 v3s16 blockpos = getNodeBlockPos(p0);
1806 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1808 modified_blocks.insert(blockpos, block);
1809 // If node emits light, MapBlock requires lighting update
1810 if(nodemgr->get(n0).light_source != 0)
1811 lighting_modified_blocks[block->getPos()] = block;
1815 enqueue neighbors for update if neccessary
1817 switch (nodemgr->get(n0.getContent()).liquid_type) {
1819 case LIQUID_FLOWING:
1820 // make sure source flows into all neighboring nodes
1821 for (u16 i = 0; i < num_flows; i++)
1822 if (flows[i].t != NEIGHBOR_UPPER)
1823 m_transforming_liquid.push_back(flows[i].p);
1824 for (u16 i = 0; i < num_airs; i++)
1825 if (airs[i].t != NEIGHBOR_UPPER)
1826 m_transforming_liquid.push_back(airs[i].p);
1829 // this flow has turned to air; neighboring flows might need to do the same
1830 for (u16 i = 0; i < num_flows; i++)
1831 m_transforming_liquid.push_back(flows[i].p);
1835 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1836 while (must_reflow.size() > 0)
1837 m_transforming_liquid.push_back(must_reflow.pop_front());
1838 updateLighting(lighting_modified_blocks, modified_blocks);
1841 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1843 v3s16 blockpos = getNodeBlockPos(p);
1844 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1845 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1847 infostream<<"Map::getNodeMetadata(): Need to emerge "
1848 <<PP(blockpos)<<std::endl;
1849 block = emergeBlock(blockpos, false);
1853 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1857 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1861 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1863 v3s16 blockpos = getNodeBlockPos(p);
1864 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1865 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1867 infostream<<"Map::setNodeMetadata(): Need to emerge "
1868 <<PP(blockpos)<<std::endl;
1869 block = emergeBlock(blockpos, false);
1873 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1877 block->m_node_metadata->set(p_rel, meta);
1880 void Map::removeNodeMetadata(v3s16 p)
1882 v3s16 blockpos = getNodeBlockPos(p);
1883 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1884 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1887 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1891 block->m_node_metadata->remove(p_rel);
1894 void Map::nodeMetadataStep(float dtime,
1895 core::map<v3s16, MapBlock*> &changed_blocks)
1899 Currently there is no way to ensure that all the necessary
1900 blocks are loaded when this is run. (They might get unloaded)
1901 NOTE: ^- Actually, that might not be so. In a quick test it
1902 reloaded a block with a furnace when I walked back to it from
1905 core::map<v2s16, MapSector*>::Iterator si;
1906 si = m_sectors.getIterator();
1907 for(; si.atEnd() == false; si++)
1909 MapSector *sector = si.getNode()->getValue();
1910 core::list< MapBlock * > sectorblocks;
1911 sector->getBlocks(sectorblocks);
1912 core::list< MapBlock * >::Iterator i;
1913 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1915 MapBlock *block = *i;
1916 bool changed = block->m_node_metadata->step(dtime);
1918 changed_blocks[block->getPos()] = block;
1927 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1928 Map(dout_server, gamedef),
1930 m_map_metadata_changed(true),
1932 m_database_read(NULL),
1933 m_database_write(NULL)
1935 verbosestream<<__FUNCTION_NAME<<std::endl;
1937 //m_chunksize = 8; // Takes a few seconds
1939 if (g_settings->get("fixed_map_seed").empty())
1941 m_seed = (((u64)(myrand()%0xffff)<<0)
1942 + ((u64)(myrand()%0xffff)<<16)
1943 + ((u64)(myrand()%0xffff)<<32)
1944 + ((u64)(myrand()%0xffff)<<48));
1948 m_seed = g_settings->getU64("fixed_map_seed");
1952 Experimental and debug stuff
1959 Try to load map; if not found, create a new one.
1962 m_savedir = savedir;
1963 m_map_saving_enabled = false;
1967 // If directory exists, check contents and load if possible
1968 if(fs::PathExists(m_savedir))
1970 // If directory is empty, it is safe to save into it.
1971 if(fs::GetDirListing(m_savedir).size() == 0)
1973 infostream<<"ServerMap: Empty save directory is valid."
1975 m_map_saving_enabled = true;
1980 // Load map metadata (seed, chunksize)
1983 catch(FileNotGoodException &e){
1984 infostream<<"WARNING: Could not load map metadata"
1985 //<<" Disabling chunk-based generator."
1990 infostream<<"ServerMap: Successfully loaded map "
1991 <<"metadata from "<<savedir
1992 <<", assuming valid save directory."
1993 <<" seed="<<m_seed<<"."
1996 m_map_saving_enabled = true;
1997 // Map loaded, not creating new one
2001 // If directory doesn't exist, it is safe to save to it
2003 m_map_saving_enabled = true;
2006 catch(std::exception &e)
2008 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2009 <<", exception: "<<e.what()<<std::endl;
2010 infostream<<"Please remove the map or fix it."<<std::endl;
2011 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2014 infostream<<"Initializing new map."<<std::endl;
2016 // Create zero sector
2017 emergeSector(v2s16(0,0));
2019 // Initially write whole map
2020 save(MOD_STATE_CLEAN);
2023 ServerMap::~ServerMap()
2025 verbosestream<<__FUNCTION_NAME<<std::endl;
2029 if(m_map_saving_enabled)
2031 // Save only changed parts
2032 save(MOD_STATE_WRITE_AT_UNLOAD);
2033 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2037 infostream<<"ServerMap: Map not saved"<<std::endl;
2040 catch(std::exception &e)
2042 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2043 <<", exception: "<<e.what()<<std::endl;
2047 Close database if it was opened
2050 sqlite3_finalize(m_database_read);
2051 if(m_database_write)
2052 sqlite3_finalize(m_database_write);
2054 sqlite3_close(m_database);
2060 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2061 for(; i.atEnd() == false; i++)
2063 MapChunk *chunk = i.getNode()->getValue();
2069 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2071 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2072 if(enable_mapgen_debug_info)
2073 infostream<<"initBlockMake(): "
2074 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2075 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2078 //s16 chunksize = 3;
2079 //v3s16 chunk_offset(-1,-1,-1);
2080 //s16 chunksize = 4;
2081 //v3s16 chunk_offset(-1,-1,-1);
2083 v3s16 chunk_offset(-2,-2,-2);
2084 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2085 v3s16 blockpos_min = blockpos_div * chunksize;
2086 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2087 blockpos_min += chunk_offset;
2088 blockpos_max += chunk_offset;
2090 //v3s16 extra_borders(1,1,1);
2091 v3s16 extra_borders(1,1,1);
2093 // Do nothing if not inside limits (+-1 because of neighbors)
2094 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2095 blockpos_over_limit(blockpos_max + extra_borders))
2101 data->no_op = false;
2102 data->seed = m_seed;
2103 data->blockpos_min = blockpos_min;
2104 data->blockpos_max = blockpos_max;
2105 data->blockpos_requested = blockpos;
2106 data->nodedef = m_gamedef->ndef();
2109 Create the whole area of this and the neighboring blocks
2112 //TimeTaker timer("initBlockMake() create area");
2114 for(s16 x=blockpos_min.X-extra_borders.X;
2115 x<=blockpos_max.X+extra_borders.X; x++)
2116 for(s16 z=blockpos_min.Z-extra_borders.Z;
2117 z<=blockpos_max.Z+extra_borders.Z; z++)
2119 v2s16 sectorpos(x, z);
2120 // Sector metadata is loaded from disk if not already loaded.
2121 ServerMapSector *sector = createSector(sectorpos);
2124 for(s16 y=blockpos_min.Y-extra_borders.Y;
2125 y<=blockpos_max.Y+extra_borders.Y; y++)
2128 //MapBlock *block = createBlock(p);
2129 // 1) get from memory, 2) load from disk
2130 MapBlock *block = emergeBlock(p, false);
2131 // 3) create a blank one
2134 block = createBlock(p);
2137 Block gets sunlight if this is true.
2139 Refer to the map generator heuristics.
2141 bool ug = mapgen::block_is_underground(data->seed, p);
2142 block->setIsUnderground(ug);
2145 // Lighting will not be valid after make_chunk is called
2146 block->setLightingExpired(true);
2147 // Lighting will be calculated
2148 //block->setLightingExpired(false);
2154 Now we have a big empty area.
2156 Make a ManualMapVoxelManipulator that contains this and the
2160 // The area that contains this block and it's neighbors
2161 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2162 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2164 data->vmanip = new ManualMapVoxelManipulator(this);
2165 //data->vmanip->setMap(this);
2169 //TimeTaker timer("initBlockMake() initialEmerge");
2170 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2173 // Data is ready now.
2176 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2177 core::map<v3s16, MapBlock*> &changed_blocks)
2179 v3s16 blockpos_min = data->blockpos_min;
2180 v3s16 blockpos_max = data->blockpos_max;
2181 v3s16 blockpos_requested = data->blockpos_requested;
2182 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2183 <<blockpos_requested.Y<<","
2184 <<blockpos_requested.Z<<")"<<std::endl;*/
2186 v3s16 extra_borders(1,1,1);
2190 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2194 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2196 /*infostream<<"Resulting vmanip:"<<std::endl;
2197 data->vmanip.print(infostream);*/
2199 // Make sure affected blocks are loaded
2200 for(s16 x=blockpos_min.X-extra_borders.X;
2201 x<=blockpos_max.X+extra_borders.X; x++)
2202 for(s16 z=blockpos_min.Z-extra_borders.Z;
2203 z<=blockpos_max.Z+extra_borders.Z; z++)
2204 for(s16 y=blockpos_min.Y-extra_borders.Y;
2205 y<=blockpos_max.Y+extra_borders.Y; y++)
2208 // Load from disk if not already in memory
2209 emergeBlock(p, false);
2213 Blit generated stuff to map
2214 NOTE: blitBackAll adds nearly everything to changed_blocks
2218 //TimeTaker timer("finishBlockMake() blitBackAll");
2219 data->vmanip->blitBackAll(&changed_blocks);
2222 if(enable_mapgen_debug_info)
2223 infostream<<"finishBlockMake: changed_blocks.size()="
2224 <<changed_blocks.size()<<std::endl;
2227 Copy transforming liquid information
2229 while(data->transforming_liquid.size() > 0)
2231 v3s16 p = data->transforming_liquid.pop_front();
2232 m_transforming_liquid.push_back(p);
2236 Do stuff in central blocks
2244 TimeTaker t("finishBlockMake lighting update");
2246 core::map<v3s16, MapBlock*> lighting_update_blocks;
2249 for(s16 x=blockpos_min.X-extra_borders.X;
2250 x<=blockpos_max.X+extra_borders.X; x++)
2251 for(s16 z=blockpos_min.Z-extra_borders.Z;
2252 z<=blockpos_max.Z+extra_borders.Z; z++)
2253 for(s16 y=blockpos_min.Y-extra_borders.Y;
2254 y<=blockpos_max.Y+extra_borders.Y; y++)
2257 MapBlock *block = getBlockNoCreateNoEx(p);
2259 lighting_update_blocks.insert(block->getPos(), block);
2262 updateLighting(lighting_update_blocks, changed_blocks);
2266 Set lighting to non-expired state in all of them.
2267 This is cheating, but it is not fast enough if all of them
2268 would actually be updated.
2270 for(s16 x=blockpos_min.X-extra_borders.X;
2271 x<=blockpos_max.X+extra_borders.X; x++)
2272 for(s16 z=blockpos_min.Z-extra_borders.Z;
2273 z<=blockpos_max.Z+extra_borders.Z; z++)
2274 for(s16 y=blockpos_min.Y-extra_borders.Y;
2275 y<=blockpos_max.Y+extra_borders.Y; y++)
2278 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2282 if(enable_mapgen_debug_info == false)
2283 t.stop(true); // Hide output
2288 Go through changed blocks
2290 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2291 i.atEnd() == false; i++)
2293 MapBlock *block = i.getNode()->getValue();
2296 Update day/night difference cache of the MapBlocks
2298 block->expireDayNightDiff();
2300 Set block as modified
2302 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2303 "finishBlockMake expireDayNightDiff");
2307 Set central blocks as generated
2309 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2310 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2311 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2314 MapBlock *block = getBlockNoCreateNoEx(p);
2316 block->setGenerated(true);
2320 Save changed parts of map
2321 NOTE: Will be saved later.
2323 //save(MOD_STATE_WRITE_AT_UNLOAD);
2325 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2326 <<","<<blockpos_requested.Y<<","
2327 <<blockpos_requested.Z<<")"<<std::endl;*/
2329 if(enable_mapgen_debug_info)
2332 Analyze resulting blocks
2334 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2335 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2336 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2337 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2338 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2339 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2341 v3s16 p = v3s16(x,y,z);
2342 MapBlock *block = getBlockNoCreateNoEx(p);
2344 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2345 infostream<<"Generated "<<spos<<": "
2346 <<analyze_block(block)<<std::endl;
2351 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2357 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2359 DSTACKF("%s: p2d=(%d,%d)",
2364 Check if it exists already in memory
2366 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2371 Try to load it from disk (with blocks)
2373 //if(loadSectorFull(p2d) == true)
2376 Try to load metadata from disk
2379 if(loadSectorMeta(p2d) == true)
2381 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2384 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2385 throw InvalidPositionException("");
2391 Do not create over-limit
2393 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2394 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2395 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2396 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2397 throw InvalidPositionException("createSector(): pos. over limit");
2400 Generate blank sector
2403 sector = new ServerMapSector(this, p2d, m_gamedef);
2405 // Sector position on map in nodes
2406 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2411 m_sectors.insert(p2d, sector);
2417 This is a quick-hand function for calling makeBlock().
2419 MapBlock * ServerMap::generateBlock(
2421 core::map<v3s16, MapBlock*> &modified_blocks
2424 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2426 /*infostream<<"generateBlock(): "
2427 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2430 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2432 TimeTaker timer("generateBlock");
2434 //MapBlock *block = original_dummy;
2436 v2s16 p2d(p.X, p.Z);
2437 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2440 Do not generate over-limit
2442 if(blockpos_over_limit(p))
2444 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2445 throw InvalidPositionException("generateBlock(): pos. over limit");
2449 Create block make data
2451 mapgen::BlockMakeData data;
2452 initBlockMake(&data, p);
2458 TimeTaker t("mapgen::make_block()");
2459 mapgen::make_block(&data);
2461 if(enable_mapgen_debug_info == false)
2462 t.stop(true); // Hide output
2466 Blit data back on map, update lighting, add mobs and whatever this does
2468 finishBlockMake(&data, modified_blocks);
2473 MapBlock *block = getBlockNoCreateNoEx(p);
2481 bool erroneus_content = false;
2482 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2483 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2484 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2487 MapNode n = block->getNode(p);
2488 if(n.getContent() == CONTENT_IGNORE)
2490 infostream<<"CONTENT_IGNORE at "
2491 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2493 erroneus_content = true;
2497 if(erroneus_content)
2506 Generate a completely empty block
2510 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2511 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2513 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2516 n.setContent(CONTENT_AIR);
2517 block->setNode(v3s16(x0,y0,z0), n);
2523 if(enable_mapgen_debug_info == false)
2524 timer.stop(true); // Hide output
2529 MapBlock * ServerMap::createBlock(v3s16 p)
2531 DSTACKF("%s: p=(%d,%d,%d)",
2532 __FUNCTION_NAME, p.X, p.Y, p.Z);
2535 Do not create over-limit
2537 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2538 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2539 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2540 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2541 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2542 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2543 throw InvalidPositionException("createBlock(): pos. over limit");
2545 v2s16 p2d(p.X, p.Z);
2548 This will create or load a sector if not found in memory.
2549 If block exists on disk, it will be loaded.
2551 NOTE: On old save formats, this will be slow, as it generates
2552 lighting on blocks for them.
2554 ServerMapSector *sector;
2556 sector = (ServerMapSector*)createSector(p2d);
2557 assert(sector->getId() == MAPSECTOR_SERVER);
2559 catch(InvalidPositionException &e)
2561 infostream<<"createBlock: createSector() failed"<<std::endl;
2565 NOTE: This should not be done, or at least the exception
2566 should not be passed on as std::exception, because it
2567 won't be catched at all.
2569 /*catch(std::exception &e)
2571 infostream<<"createBlock: createSector() failed: "
2572 <<e.what()<<std::endl;
2577 Try to get a block from the sector
2580 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2583 if(block->isDummy())
2588 block = sector->createBlankBlock(block_y);
2592 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2594 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2596 p.X, p.Y, p.Z, allow_generate);
2599 MapBlock *block = getBlockNoCreateNoEx(p);
2600 if(block && block->isDummy() == false)
2605 MapBlock *block = loadBlock(p);
2612 core::map<v3s16, MapBlock*> modified_blocks;
2613 MapBlock *block = generateBlock(p, modified_blocks);
2617 event.type = MEET_OTHER;
2620 // Copy modified_blocks to event
2621 for(core::map<v3s16, MapBlock*>::Iterator
2622 i = modified_blocks.getIterator();
2623 i.atEnd()==false; i++)
2625 event.modified_blocks.insert(i.getNode()->getKey(), false);
2629 dispatchEvent(&event);
2638 s16 ServerMap::findGroundLevel(v2s16 p2d)
2642 Uh, just do something random...
2644 // Find existing map from top to down
2647 v3s16 p(p2d.X, max, p2d.Y);
2648 for(; p.Y>min; p.Y--)
2650 MapNode n = getNodeNoEx(p);
2651 if(n.getContent() != CONTENT_IGNORE)
2656 // If this node is not air, go to plan b
2657 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2659 // Search existing walkable and return it
2660 for(; p.Y>min; p.Y--)
2662 MapNode n = getNodeNoEx(p);
2663 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2672 Determine from map generator noise functions
2675 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2678 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2679 //return (s16)level;
2682 void ServerMap::createDatabase() {
2685 e = sqlite3_exec(m_database,
2686 "CREATE TABLE IF NOT EXISTS `blocks` ("
2687 "`pos` INT NOT NULL PRIMARY KEY,"
2690 , NULL, NULL, NULL);
2691 if(e == SQLITE_ABORT)
2692 throw FileNotGoodException("Could not create database structure");
2694 infostream<<"ServerMap: Database structure was created";
2697 void ServerMap::verifyDatabase() {
2702 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2703 bool needs_create = false;
2707 Open the database connection
2710 createDirs(m_savedir);
2712 if(!fs::PathExists(dbp))
2713 needs_create = true;
2715 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2716 if(d != SQLITE_OK) {
2717 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2718 throw FileNotGoodException("Cannot open database file");
2724 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2725 if(d != SQLITE_OK) {
2726 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2727 throw FileNotGoodException("Cannot prepare read statement");
2730 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2731 if(d != SQLITE_OK) {
2732 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2733 throw FileNotGoodException("Cannot prepare write statement");
2736 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2737 if(d != SQLITE_OK) {
2738 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2739 throw FileNotGoodException("Cannot prepare read statement");
2742 infostream<<"ServerMap: Database opened"<<std::endl;
2746 bool ServerMap::loadFromFolders() {
2747 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2752 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2753 return (sqlite3_int64)pos.Z*16777216 +
2754 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2757 void ServerMap::createDirs(std::string path)
2759 if(fs::CreateAllDirs(path) == false)
2761 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2762 <<"\""<<path<<"\""<<std::endl;
2763 throw BaseException("ServerMap failed to create directory");
2767 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2773 snprintf(cc, 9, "%.4x%.4x",
2774 (unsigned int)pos.X&0xffff,
2775 (unsigned int)pos.Y&0xffff);
2777 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2779 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2780 (unsigned int)pos.X&0xfff,
2781 (unsigned int)pos.Y&0xfff);
2783 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2789 v2s16 ServerMap::getSectorPos(std::string dirname)
2793 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2794 assert(spos != std::string::npos);
2795 if(dirname.size() - spos == 8)
2798 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2800 else if(dirname.size() - spos == 3)
2803 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2804 // Sign-extend the 12 bit values up to 16 bits...
2805 if(x&0x800) x|=0xF000;
2806 if(y&0x800) y|=0xF000;
2813 v2s16 pos((s16)x, (s16)y);
2817 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2819 v2s16 p2d = getSectorPos(sectordir);
2821 if(blockfile.size() != 4){
2822 throw InvalidFilenameException("Invalid block filename");
2825 int r = sscanf(blockfile.c_str(), "%4x", &y);
2827 throw InvalidFilenameException("Invalid block filename");
2828 return v3s16(p2d.X, y, p2d.Y);
2831 std::string ServerMap::getBlockFilename(v3s16 p)
2834 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2838 void ServerMap::save(ModifiedState save_level)
2840 DSTACK(__FUNCTION_NAME);
2841 if(m_map_saving_enabled == false)
2843 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2847 if(save_level == MOD_STATE_CLEAN)
2848 infostream<<"ServerMap: Saving whole map, this can take time."
2851 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2856 // Profile modified reasons
2857 Profiler modprofiler;
2859 u32 sector_meta_count = 0;
2860 u32 block_count = 0;
2861 u32 block_count_all = 0; // Number of blocks in memory
2863 // Don't do anything with sqlite unless something is really saved
2864 bool save_started = false;
2866 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2867 for(; i.atEnd() == false; i++)
2869 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2870 assert(sector->getId() == MAPSECTOR_SERVER);
2872 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2874 saveSectorMeta(sector);
2875 sector_meta_count++;
2877 core::list<MapBlock*> blocks;
2878 sector->getBlocks(blocks);
2879 core::list<MapBlock*>::Iterator j;
2881 for(j=blocks.begin(); j!=blocks.end(); j++)
2883 MapBlock *block = *j;
2887 if(block->getModified() >= save_level)
2892 save_started = true;
2895 modprofiler.add(block->getModifiedReason(), 1);
2900 /*infostream<<"ServerMap: Written block ("
2901 <<block->getPos().X<<","
2902 <<block->getPos().Y<<","
2903 <<block->getPos().Z<<")"
2912 Only print if something happened or saved whole map
2914 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2915 || block_count != 0)
2917 infostream<<"ServerMap: Written: "
2918 <<sector_meta_count<<" sector metadata files, "
2919 <<block_count<<" block files"
2920 <<", "<<block_count_all<<" blocks in memory."
2922 PrintInfo(infostream); // ServerMap/ClientMap:
2923 infostream<<"Blocks modified by: "<<std::endl;
2924 modprofiler.print(infostream);
2928 static s32 unsignedToSigned(s32 i, s32 max_positive)
2930 if(i < max_positive)
2933 return i - 2*max_positive;
2936 // modulo of a negative number does not work consistently in C
2937 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2941 return mod - ((-i) % mod);
2944 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2946 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2948 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2950 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2951 return v3s16(x,y,z);
2954 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2956 if(loadFromFolders()){
2957 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2958 <<"all blocks that are stored in flat files"<<std::endl;
2964 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2966 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2967 v3s16 p = getIntegerAsBlock(block_i);
2968 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2974 void ServerMap::saveMapMeta()
2976 DSTACK(__FUNCTION_NAME);
2978 /*infostream<<"ServerMap::saveMapMeta(): "
2982 createDirs(m_savedir);
2984 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2985 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2986 if(os.good() == false)
2988 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2989 <<"could not open"<<fullpath<<std::endl;
2990 throw FileNotGoodException("Cannot open chunk metadata");
2994 params.setU64("seed", m_seed);
2996 params.writeLines(os);
2998 os<<"[end_of_params]\n";
3000 m_map_metadata_changed = false;
3003 void ServerMap::loadMapMeta()
3005 DSTACK(__FUNCTION_NAME);
3007 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3010 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3011 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3012 if(is.good() == false)
3014 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3015 <<"could not open"<<fullpath<<std::endl;
3016 throw FileNotGoodException("Cannot open map metadata");
3024 throw SerializationError
3025 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3027 std::getline(is, line);
3028 std::string trimmedline = trim(line);
3029 if(trimmedline == "[end_of_params]")
3031 params.parseConfigLine(line);
3034 m_seed = params.getU64("seed");
3036 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3039 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3041 DSTACK(__FUNCTION_NAME);
3042 // Format used for writing
3043 u8 version = SER_FMT_VER_HIGHEST;
3045 v2s16 pos = sector->getPos();
3046 std::string dir = getSectorDir(pos);
3049 std::string fullpath = dir + DIR_DELIM + "meta";
3050 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3051 if(o.good() == false)
3052 throw FileNotGoodException("Cannot open sector metafile");
3054 sector->serialize(o, version);
3056 sector->differs_from_disk = false;
3059 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3061 DSTACK(__FUNCTION_NAME);
3063 v2s16 p2d = getSectorPos(sectordir);
3065 ServerMapSector *sector = NULL;
3067 std::string fullpath = sectordir + DIR_DELIM + "meta";
3068 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3069 if(is.good() == false)
3071 // If the directory exists anyway, it probably is in some old
3072 // format. Just go ahead and create the sector.
3073 if(fs::PathExists(sectordir))
3075 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3076 <<fullpath<<" doesn't exist but directory does."
3077 <<" Continuing with a sector with no metadata."
3079 sector = new ServerMapSector(this, p2d, m_gamedef);
3080 m_sectors.insert(p2d, sector);
3084 throw FileNotGoodException("Cannot open sector metafile");
3089 sector = ServerMapSector::deSerialize
3090 (is, this, p2d, m_sectors, m_gamedef);
3092 saveSectorMeta(sector);
3095 sector->differs_from_disk = false;
3100 bool ServerMap::loadSectorMeta(v2s16 p2d)
3102 DSTACK(__FUNCTION_NAME);
3104 MapSector *sector = NULL;
3106 // The directory layout we're going to load from.
3107 // 1 - original sectors/xxxxzzzz/
3108 // 2 - new sectors2/xxx/zzz/
3109 // If we load from anything but the latest structure, we will
3110 // immediately save to the new one, and remove the old.
3112 std::string sectordir1 = getSectorDir(p2d, 1);
3113 std::string sectordir;
3114 if(fs::PathExists(sectordir1))
3116 sectordir = sectordir1;
3121 sectordir = getSectorDir(p2d, 2);
3125 sector = loadSectorMeta(sectordir, loadlayout != 2);
3127 catch(InvalidFilenameException &e)
3131 catch(FileNotGoodException &e)
3135 catch(std::exception &e)
3144 bool ServerMap::loadSectorFull(v2s16 p2d)
3146 DSTACK(__FUNCTION_NAME);
3148 MapSector *sector = NULL;
3150 // The directory layout we're going to load from.
3151 // 1 - original sectors/xxxxzzzz/
3152 // 2 - new sectors2/xxx/zzz/
3153 // If we load from anything but the latest structure, we will
3154 // immediately save to the new one, and remove the old.
3156 std::string sectordir1 = getSectorDir(p2d, 1);
3157 std::string sectordir;
3158 if(fs::PathExists(sectordir1))
3160 sectordir = sectordir1;
3165 sectordir = getSectorDir(p2d, 2);
3169 sector = loadSectorMeta(sectordir, loadlayout != 2);
3171 catch(InvalidFilenameException &e)
3175 catch(FileNotGoodException &e)
3179 catch(std::exception &e)
3187 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3189 std::vector<fs::DirListNode>::iterator i2;
3190 for(i2=list2.begin(); i2!=list2.end(); i2++)
3196 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3198 catch(InvalidFilenameException &e)
3200 // This catches unknown crap in directory
3206 infostream<<"Sector converted to new layout - deleting "<<
3207 sectordir1<<std::endl;
3208 fs::RecursiveDelete(sectordir1);
3215 void ServerMap::beginSave() {
3217 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3218 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3221 void ServerMap::endSave() {
3223 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3224 infostream<<"WARNING: endSave() failed, map might not have saved.";
3227 void ServerMap::saveBlock(MapBlock *block)
3229 DSTACK(__FUNCTION_NAME);
3231 Dummy blocks are not written
3233 if(block->isDummy())
3235 /*v3s16 p = block->getPos();
3236 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3237 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3241 // Format used for writing
3242 u8 version = SER_FMT_VER_HIGHEST;
3244 v3s16 p3d = block->getPos();
3248 v2s16 p2d(p3d.X, p3d.Z);
3249 std::string sectordir = getSectorDir(p2d);
3251 createDirs(sectordir);
3253 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3254 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3255 if(o.good() == false)
3256 throw FileNotGoodException("Cannot open block data");
3259 [0] u8 serialization version
3265 std::ostringstream o(std::ios_base::binary);
3267 o.write((char*)&version, 1);
3270 block->serialize(o, version, true);
3272 // Write block to database
3274 std::string tmp = o.str();
3275 const char *bytes = tmp.c_str();
3277 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3278 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3279 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3280 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3281 int written = sqlite3_step(m_database_write);
3282 if(written != SQLITE_DONE)
3283 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3284 <<sqlite3_errmsg(m_database)<<std::endl;
3285 // Make ready for later reuse
3286 sqlite3_reset(m_database_write);
3288 // We just wrote it to the disk so clear modified flag
3289 block->resetModified();
3292 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3294 DSTACK(__FUNCTION_NAME);
3296 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3299 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3300 if(is.good() == false)
3301 throw FileNotGoodException("Cannot open block file");
3303 v3s16 p3d = getBlockPos(sectordir, blockfile);
3304 v2s16 p2d(p3d.X, p3d.Z);
3306 assert(sector->getPos() == p2d);
3308 u8 version = SER_FMT_VER_INVALID;
3309 is.read((char*)&version, 1);
3312 throw SerializationError("ServerMap::loadBlock(): Failed"
3313 " to read MapBlock version");
3315 /*u32 block_size = MapBlock::serializedLength(version);
3316 SharedBuffer<u8> data(block_size);
3317 is.read((char*)*data, block_size);*/
3319 // This will always return a sector because we're the server
3320 //MapSector *sector = emergeSector(p2d);
3322 MapBlock *block = NULL;
3323 bool created_new = false;
3324 block = sector->getBlockNoCreateNoEx(p3d.Y);
3327 block = sector->createBlankBlockNoInsert(p3d.Y);
3332 block->deSerialize(is, version, true);
3334 // If it's a new block, insert it to the map
3336 sector->insertBlock(block);
3339 Save blocks loaded in old format in new format
3342 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3346 // Should be in database now, so delete the old file
3347 fs::RecursiveDelete(fullpath);
3350 // We just loaded it from the disk, so it's up-to-date.
3351 block->resetModified();
3354 catch(SerializationError &e)
3356 infostream<<"WARNING: Invalid block data on disk "
3357 <<"fullpath="<<fullpath
3358 <<" (SerializationError). "
3359 <<"what()="<<e.what()
3361 //" Ignoring. A new one will be generated.
3364 // TODO: Backup file; name is in fullpath.
3368 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3370 DSTACK(__FUNCTION_NAME);
3373 std::istringstream is(*blob, std::ios_base::binary);
3375 u8 version = SER_FMT_VER_INVALID;
3376 is.read((char*)&version, 1);
3379 throw SerializationError("ServerMap::loadBlock(): Failed"
3380 " to read MapBlock version");
3382 /*u32 block_size = MapBlock::serializedLength(version);
3383 SharedBuffer<u8> data(block_size);
3384 is.read((char*)*data, block_size);*/
3386 // This will always return a sector because we're the server
3387 //MapSector *sector = emergeSector(p2d);
3389 MapBlock *block = NULL;
3390 bool created_new = false;
3391 block = sector->getBlockNoCreateNoEx(p3d.Y);
3394 block = sector->createBlankBlockNoInsert(p3d.Y);
3399 block->deSerialize(is, version, true);
3401 // If it's a new block, insert it to the map
3403 sector->insertBlock(block);
3406 Save blocks loaded in old format in new format
3409 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3410 // Only save if asked to; no need to update version
3414 // We just loaded it from, so it's up-to-date.
3415 block->resetModified();
3418 catch(SerializationError &e)
3420 infostream<<"WARNING: Invalid block data in database "
3421 <<" (SerializationError). "
3422 <<"what()="<<e.what()
3424 //" Ignoring. A new one will be generated.
3427 // TODO: Copy to a backup database.
3431 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3433 DSTACK(__FUNCTION_NAME);
3435 v2s16 p2d(blockpos.X, blockpos.Z);
3437 if(!loadFromFolders()) {
3440 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3441 infostream<<"WARNING: Could not bind block position for load: "
3442 <<sqlite3_errmsg(m_database)<<std::endl;
3443 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3445 Make sure sector is loaded
3447 MapSector *sector = createSector(p2d);
3452 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3453 size_t len = sqlite3_column_bytes(m_database_read, 0);
3455 std::string datastr(data, len);
3457 loadBlock(&datastr, blockpos, sector, false);
3459 sqlite3_step(m_database_read);
3460 // We should never get more than 1 row, so ok to reset
3461 sqlite3_reset(m_database_read);
3463 return getBlockNoCreateNoEx(blockpos);
3465 sqlite3_reset(m_database_read);
3467 // Not found in database, try the files
3470 // The directory layout we're going to load from.
3471 // 1 - original sectors/xxxxzzzz/
3472 // 2 - new sectors2/xxx/zzz/
3473 // If we load from anything but the latest structure, we will
3474 // immediately save to the new one, and remove the old.
3476 std::string sectordir1 = getSectorDir(p2d, 1);
3477 std::string sectordir;
3478 if(fs::PathExists(sectordir1))
3480 sectordir = sectordir1;
3485 sectordir = getSectorDir(p2d, 2);
3489 Make sure sector is loaded
3491 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3495 sector = loadSectorMeta(sectordir, loadlayout != 2);
3497 catch(InvalidFilenameException &e)
3501 catch(FileNotGoodException &e)
3505 catch(std::exception &e)
3512 Make sure file exists
3515 std::string blockfilename = getBlockFilename(blockpos);
3516 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3520 Load block and save it to the database
3522 loadBlock(sectordir, blockfilename, sector, true);
3523 return getBlockNoCreateNoEx(blockpos);
3526 void ServerMap::PrintInfo(std::ostream &out)
3535 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3540 MapVoxelManipulator::~MapVoxelManipulator()
3542 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3546 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3548 TimeTaker timer1("emerge", &emerge_time);
3550 // Units of these are MapBlocks
3551 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3552 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3554 VoxelArea block_area_nodes
3555 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3557 addArea(block_area_nodes);
3559 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3560 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3561 for(s32 x=p_min.X; x<=p_max.X; x++)
3564 core::map<v3s16, bool>::Node *n;
3565 n = m_loaded_blocks.find(p);
3569 bool block_data_inexistent = false;
3572 TimeTaker timer1("emerge load", &emerge_load_time);
3574 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3575 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3577 a.print(infostream);
3578 infostream<<std::endl;*/
3580 MapBlock *block = m_map->getBlockNoCreate(p);
3581 if(block->isDummy())
3582 block_data_inexistent = true;
3584 block->copyTo(*this);
3586 catch(InvalidPositionException &e)
3588 block_data_inexistent = true;
3591 if(block_data_inexistent)
3593 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3594 // Fill with VOXELFLAG_INEXISTENT
3595 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3596 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3598 s32 i = m_area.index(a.MinEdge.X,y,z);
3599 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3603 m_loaded_blocks.insert(p, !block_data_inexistent);
3606 //infostream<<"emerge done"<<std::endl;
3610 SUGG: Add an option to only update eg. water and air nodes.
3611 This will make it interfere less with important stuff if
3614 void MapVoxelManipulator::blitBack
3615 (core::map<v3s16, MapBlock*> & modified_blocks)
3617 if(m_area.getExtent() == v3s16(0,0,0))
3620 //TimeTaker timer1("blitBack");
3622 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3623 <<m_loaded_blocks.size()<<std::endl;*/
3626 Initialize block cache
3628 v3s16 blockpos_last;
3629 MapBlock *block = NULL;
3630 bool block_checked_in_modified = false;
3632 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3633 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3634 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3638 u8 f = m_flags[m_area.index(p)];
3639 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3642 MapNode &n = m_data[m_area.index(p)];
3644 v3s16 blockpos = getNodeBlockPos(p);
3649 if(block == NULL || blockpos != blockpos_last){
3650 block = m_map->getBlockNoCreate(blockpos);
3651 blockpos_last = blockpos;
3652 block_checked_in_modified = false;
3655 // Calculate relative position in block
3656 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3658 // Don't continue if nothing has changed here
3659 if(block->getNode(relpos) == n)
3662 //m_map->setNode(m_area.MinEdge + p, n);
3663 block->setNode(relpos, n);
3666 Make sure block is in modified_blocks
3668 if(block_checked_in_modified == false)
3670 modified_blocks[blockpos] = block;
3671 block_checked_in_modified = true;
3674 catch(InvalidPositionException &e)
3680 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3681 MapVoxelManipulator(map),
3682 m_create_area(false)
3686 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3690 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3692 // Just create the area so that it can be pointed to
3693 VoxelManipulator::emerge(a, caller_id);
3696 void ManualMapVoxelManipulator::initialEmerge(
3697 v3s16 blockpos_min, v3s16 blockpos_max)
3699 TimeTaker timer1("initialEmerge", &emerge_time);
3701 // Units of these are MapBlocks
3702 v3s16 p_min = blockpos_min;
3703 v3s16 p_max = blockpos_max;
3705 VoxelArea block_area_nodes
3706 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3708 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3711 infostream<<"initialEmerge: area: ";
3712 block_area_nodes.print(infostream);
3713 infostream<<" ("<<size_MB<<"MB)";
3714 infostream<<std::endl;
3717 addArea(block_area_nodes);
3719 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3720 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3721 for(s32 x=p_min.X; x<=p_max.X; x++)
3724 core::map<v3s16, bool>::Node *n;
3725 n = m_loaded_blocks.find(p);
3729 bool block_data_inexistent = false;
3732 TimeTaker timer1("emerge load", &emerge_load_time);
3734 MapBlock *block = m_map->getBlockNoCreate(p);
3735 if(block->isDummy())
3736 block_data_inexistent = true;
3738 block->copyTo(*this);
3740 catch(InvalidPositionException &e)
3742 block_data_inexistent = true;
3745 if(block_data_inexistent)
3748 Mark area inexistent
3750 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3751 // Fill with VOXELFLAG_INEXISTENT
3752 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3753 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3755 s32 i = m_area.index(a.MinEdge.X,y,z);
3756 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3760 m_loaded_blocks.insert(p, !block_data_inexistent);
3764 void ManualMapVoxelManipulator::blitBackAll(
3765 core::map<v3s16, MapBlock*> * modified_blocks)
3767 if(m_area.getExtent() == v3s16(0,0,0))
3771 Copy data of all blocks
3773 for(core::map<v3s16, bool>::Iterator
3774 i = m_loaded_blocks.getIterator();
3775 i.atEnd() == false; i++)
3777 v3s16 p = i.getNode()->getKey();
3778 bool existed = i.getNode()->getValue();
3779 if(existed == false)
3781 // The Great Bug was found using this
3782 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3783 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3787 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3790 infostream<<"WARNING: "<<__FUNCTION_NAME
3791 <<": got NULL block "
3792 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3797 block->copyFrom(*this);
3800 modified_blocks->insert(p, block);