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)
1301 event.type = MEET_ADDNODE;
1305 bool succeeded = true;
1307 core::map<v3s16, MapBlock*> modified_blocks;
1308 addNodeAndUpdate(p, n, modified_blocks);
1310 // Copy modified_blocks to event
1311 for(core::map<v3s16, MapBlock*>::Iterator
1312 i = modified_blocks.getIterator();
1313 i.atEnd()==false; i++)
1315 event.modified_blocks.insert(i.getNode()->getKey(), false);
1318 catch(InvalidPositionException &e){
1322 dispatchEvent(&event);
1327 bool Map::removeNodeWithEvent(v3s16 p)
1330 event.type = MEET_REMOVENODE;
1333 bool succeeded = true;
1335 core::map<v3s16, MapBlock*> modified_blocks;
1336 removeNodeAndUpdate(p, modified_blocks);
1338 // Copy modified_blocks to event
1339 for(core::map<v3s16, MapBlock*>::Iterator
1340 i = modified_blocks.getIterator();
1341 i.atEnd()==false; i++)
1343 event.modified_blocks.insert(i.getNode()->getKey(), false);
1346 catch(InvalidPositionException &e){
1350 dispatchEvent(&event);
1355 bool Map::getDayNightDiff(v3s16 blockpos)
1358 v3s16 p = blockpos + v3s16(0,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->getDayNightDiff())
1363 catch(InvalidPositionException &e){}
1366 v3s16 p = blockpos + v3s16(-1,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->getDayNightDiff())
1371 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(0,-1,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->getDayNightDiff())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,0,-1);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->getDayNightDiff())
1385 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(1,0,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->getDayNightDiff())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,1,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->getDayNightDiff())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,0,1);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->getDayNightDiff())
1407 catch(InvalidPositionException &e){}
1413 Updates usage timers
1415 void Map::timerUpdate(float dtime, float unload_timeout,
1416 core::list<v3s16> *unloaded_blocks)
1418 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1420 // Profile modified reasons
1421 Profiler modprofiler;
1423 core::list<v2s16> sector_deletion_queue;
1424 u32 deleted_blocks_count = 0;
1425 u32 saved_blocks_count = 0;
1426 u32 block_count_all = 0;
1428 core::map<v2s16, MapSector*>::Iterator si;
1431 si = m_sectors.getIterator();
1432 for(; si.atEnd() == false; si++)
1434 MapSector *sector = si.getNode()->getValue();
1436 bool all_blocks_deleted = true;
1438 core::list<MapBlock*> blocks;
1439 sector->getBlocks(blocks);
1441 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1442 i != blocks.end(); i++)
1444 MapBlock *block = (*i);
1446 block->incrementUsageTimer(dtime);
1448 if(block->getUsageTimer() > unload_timeout)
1450 v3s16 p = block->getPos();
1453 if(block->getModified() != MOD_STATE_CLEAN
1454 && save_before_unloading)
1456 modprofiler.add(block->getModifiedReason(), 1);
1458 saved_blocks_count++;
1461 // Delete from memory
1462 sector->deleteBlock(block);
1465 unloaded_blocks->push_back(p);
1467 deleted_blocks_count++;
1471 all_blocks_deleted = false;
1476 if(all_blocks_deleted)
1478 sector_deletion_queue.push_back(si.getNode()->getKey());
1483 // Finally delete the empty sectors
1484 deleteSectors(sector_deletion_queue);
1486 if(deleted_blocks_count != 0)
1488 PrintInfo(infostream); // ServerMap/ClientMap:
1489 infostream<<"Unloaded "<<deleted_blocks_count
1490 <<" blocks from memory";
1491 if(save_before_unloading)
1492 infostream<<", of which "<<saved_blocks_count<<" were written";
1493 infostream<<", "<<block_count_all<<" blocks in memory";
1494 infostream<<"."<<std::endl;
1495 if(saved_blocks_count != 0){
1496 PrintInfo(infostream); // ServerMap/ClientMap:
1497 infostream<<"Blocks modified by: "<<std::endl;
1498 modprofiler.print(infostream);
1503 void Map::deleteSectors(core::list<v2s16> &list)
1505 core::list<v2s16>::Iterator j;
1506 for(j=list.begin(); j!=list.end(); j++)
1508 MapSector *sector = m_sectors[*j];
1509 // If sector is in sector cache, remove it from there
1510 if(m_sector_cache == sector)
1511 m_sector_cache = NULL;
1512 // Remove from map and delete
1513 m_sectors.remove(*j);
1519 void Map::unloadUnusedData(float timeout,
1520 core::list<v3s16> *deleted_blocks)
1522 core::list<v2s16> sector_deletion_queue;
1523 u32 deleted_blocks_count = 0;
1524 u32 saved_blocks_count = 0;
1526 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1527 for(; si.atEnd() == false; si++)
1529 MapSector *sector = si.getNode()->getValue();
1531 bool all_blocks_deleted = true;
1533 core::list<MapBlock*> blocks;
1534 sector->getBlocks(blocks);
1535 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1536 i != blocks.end(); i++)
1538 MapBlock *block = (*i);
1540 if(block->getUsageTimer() > timeout)
1543 if(block->getModified() != MOD_STATE_CLEAN)
1546 saved_blocks_count++;
1548 // Delete from memory
1549 sector->deleteBlock(block);
1550 deleted_blocks_count++;
1554 all_blocks_deleted = false;
1558 if(all_blocks_deleted)
1560 sector_deletion_queue.push_back(si.getNode()->getKey());
1564 deleteSectors(sector_deletion_queue);
1566 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1567 <<", of which "<<saved_blocks_count<<" were wr."
1570 //return sector_deletion_queue.getSize();
1571 //return deleted_blocks_count;
1575 void Map::PrintInfo(std::ostream &out)
1580 #define WATER_DROP_BOOST 4
1584 NEIGHBOR_SAME_LEVEL,
1587 struct NodeNeighbor {
1593 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1595 INodeDefManager *nodemgr = m_gamedef->ndef();
1597 DSTACK(__FUNCTION_NAME);
1598 //TimeTaker timer("transformLiquids()");
1601 u32 initial_size = m_transforming_liquid.size();
1603 /*if(initial_size != 0)
1604 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1606 // list of nodes that due to viscosity have not reached their max level height
1607 UniqueQueue<v3s16> must_reflow;
1609 // List of MapBlocks that will require a lighting update (due to lava)
1610 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1612 while(m_transforming_liquid.size() != 0)
1614 // This should be done here so that it is done when continue is used
1615 if(loopcount >= initial_size * 3)
1620 Get a queued transforming liquid node
1622 v3s16 p0 = m_transforming_liquid.pop_front();
1624 MapNode n0 = getNodeNoEx(p0);
1627 Collect information about current node
1629 s8 liquid_level = -1;
1630 u8 liquid_kind = CONTENT_IGNORE;
1631 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1632 switch (liquid_type) {
1634 liquid_level = LIQUID_LEVEL_SOURCE;
1635 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1637 case LIQUID_FLOWING:
1638 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1639 liquid_kind = n0.getContent();
1642 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1643 // continue with the next node.
1644 if (n0.getContent() != CONTENT_AIR)
1646 liquid_kind = CONTENT_AIR;
1651 Collect information about the environment
1653 const v3s16 *dirs = g_6dirs;
1654 NodeNeighbor sources[6]; // surrounding sources
1655 int num_sources = 0;
1656 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1658 NodeNeighbor airs[6]; // surrounding air
1660 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1661 int num_neutrals = 0;
1662 bool flowing_down = false;
1663 for (u16 i = 0; i < 6; i++) {
1664 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1667 nt = NEIGHBOR_UPPER;
1670 nt = NEIGHBOR_LOWER;
1673 v3s16 npos = p0 + dirs[i];
1674 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1675 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1677 if (nb.n.getContent() == CONTENT_AIR) {
1678 airs[num_airs++] = nb;
1679 // if the current node is a water source the neighbor
1680 // should be enqueded for transformation regardless of whether the
1681 // current node changes or not.
1682 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1683 m_transforming_liquid.push_back(npos);
1684 // if the current node happens to be a flowing node, it will start to flow down here.
1685 if (nb.t == NEIGHBOR_LOWER) {
1686 flowing_down = true;
1689 neutrals[num_neutrals++] = nb;
1693 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1694 if (liquid_kind == CONTENT_AIR)
1695 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1696 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1697 neutrals[num_neutrals++] = nb;
1699 // Do not count bottom source, it will screw things up
1701 sources[num_sources++] = nb;
1704 case LIQUID_FLOWING:
1705 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1706 if (liquid_kind == CONTENT_AIR)
1707 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1708 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1709 neutrals[num_neutrals++] = nb;
1711 flows[num_flows++] = nb;
1712 if (nb.t == NEIGHBOR_LOWER)
1713 flowing_down = true;
1720 decide on the type (and possibly level) of the current node
1722 content_t new_node_content;
1723 s8 new_node_level = -1;
1724 s8 max_node_level = -1;
1725 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1726 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1727 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1728 // it's perfectly safe to use liquid_kind here to determine the new node content.
1729 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1730 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1731 // liquid_kind is set properly, see above
1732 new_node_content = liquid_kind;
1733 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1735 // no surrounding sources, so get the maximum level that can flow into this node
1736 for (u16 i = 0; i < num_flows; i++) {
1737 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1738 switch (flows[i].t) {
1739 case NEIGHBOR_UPPER:
1740 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1741 max_node_level = LIQUID_LEVEL_MAX;
1742 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1743 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1744 } else if (nb_liquid_level > max_node_level)
1745 max_node_level = nb_liquid_level;
1747 case NEIGHBOR_LOWER:
1749 case NEIGHBOR_SAME_LEVEL:
1750 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1751 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1752 max_node_level = nb_liquid_level - 1;
1758 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1759 if (viscosity > 1 && max_node_level != liquid_level) {
1760 // amount to gain, limited by viscosity
1761 // must be at least 1 in absolute value
1762 s8 level_inc = max_node_level - liquid_level;
1763 if (level_inc < -viscosity || level_inc > viscosity)
1764 new_node_level = liquid_level + level_inc/viscosity;
1765 else if (level_inc < 0)
1766 new_node_level = liquid_level - 1;
1767 else if (level_inc > 0)
1768 new_node_level = liquid_level + 1;
1769 if (new_node_level != max_node_level)
1770 must_reflow.push_back(p0);
1772 new_node_level = max_node_level;
1774 if (new_node_level >= 0)
1775 new_node_content = liquid_kind;
1777 new_node_content = CONTENT_AIR;
1782 check if anything has changed. if not, just continue with the next node.
1784 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1785 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1786 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1792 update the current node
1794 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1795 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1796 // set level to last 3 bits, flowing down bit to 4th bit
1797 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1799 // set the liquid level and flow bit to 0
1800 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1802 n0.setContent(new_node_content);
1804 v3s16 blockpos = getNodeBlockPos(p0);
1805 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1807 modified_blocks.insert(blockpos, block);
1808 // If node emits light, MapBlock requires lighting update
1809 if(nodemgr->get(n0).light_source != 0)
1810 lighting_modified_blocks[block->getPos()] = block;
1814 enqueue neighbors for update if neccessary
1816 switch (nodemgr->get(n0.getContent()).liquid_type) {
1818 case LIQUID_FLOWING:
1819 // make sure source flows into all neighboring nodes
1820 for (u16 i = 0; i < num_flows; i++)
1821 if (flows[i].t != NEIGHBOR_UPPER)
1822 m_transforming_liquid.push_back(flows[i].p);
1823 for (u16 i = 0; i < num_airs; i++)
1824 if (airs[i].t != NEIGHBOR_UPPER)
1825 m_transforming_liquid.push_back(airs[i].p);
1828 // this flow has turned to air; neighboring flows might need to do the same
1829 for (u16 i = 0; i < num_flows; i++)
1830 m_transforming_liquid.push_back(flows[i].p);
1834 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1835 while (must_reflow.size() > 0)
1836 m_transforming_liquid.push_back(must_reflow.pop_front());
1837 updateLighting(lighting_modified_blocks, modified_blocks);
1840 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1842 v3s16 blockpos = getNodeBlockPos(p);
1843 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 infostream<<"Map::getNodeMetadata(): Need to emerge "
1847 <<PP(blockpos)<<std::endl;
1848 block = emergeBlock(blockpos, false);
1852 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1856 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1860 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1862 v3s16 blockpos = getNodeBlockPos(p);
1863 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1864 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1866 infostream<<"Map::setNodeMetadata(): Need to emerge "
1867 <<PP(blockpos)<<std::endl;
1868 block = emergeBlock(blockpos, false);
1872 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1876 block->m_node_metadata->set(p_rel, meta);
1879 void Map::removeNodeMetadata(v3s16 p)
1881 v3s16 blockpos = getNodeBlockPos(p);
1882 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1883 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1886 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1890 block->m_node_metadata->remove(p_rel);
1893 void Map::nodeMetadataStep(float dtime,
1894 core::map<v3s16, MapBlock*> &changed_blocks)
1898 Currently there is no way to ensure that all the necessary
1899 blocks are loaded when this is run. (They might get unloaded)
1900 NOTE: ^- Actually, that might not be so. In a quick test it
1901 reloaded a block with a furnace when I walked back to it from
1904 core::map<v2s16, MapSector*>::Iterator si;
1905 si = m_sectors.getIterator();
1906 for(; si.atEnd() == false; si++)
1908 MapSector *sector = si.getNode()->getValue();
1909 core::list< MapBlock * > sectorblocks;
1910 sector->getBlocks(sectorblocks);
1911 core::list< MapBlock * >::Iterator i;
1912 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1914 MapBlock *block = *i;
1915 bool changed = block->m_node_metadata->step(dtime);
1917 changed_blocks[block->getPos()] = block;
1926 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1927 Map(dout_server, gamedef),
1929 m_map_metadata_changed(true),
1931 m_database_read(NULL),
1932 m_database_write(NULL)
1934 verbosestream<<__FUNCTION_NAME<<std::endl;
1936 //m_chunksize = 8; // Takes a few seconds
1938 if (g_settings->get("fixed_map_seed").empty())
1940 m_seed = (((u64)(myrand()%0xffff)<<0)
1941 + ((u64)(myrand()%0xffff)<<16)
1942 + ((u64)(myrand()%0xffff)<<32)
1943 + ((u64)(myrand()%0xffff)<<48));
1947 m_seed = g_settings->getU64("fixed_map_seed");
1951 Experimental and debug stuff
1958 Try to load map; if not found, create a new one.
1961 m_savedir = savedir;
1962 m_map_saving_enabled = false;
1966 // If directory exists, check contents and load if possible
1967 if(fs::PathExists(m_savedir))
1969 // If directory is empty, it is safe to save into it.
1970 if(fs::GetDirListing(m_savedir).size() == 0)
1972 infostream<<"ServerMap: Empty save directory is valid."
1974 m_map_saving_enabled = true;
1979 // Load map metadata (seed, chunksize)
1982 catch(FileNotGoodException &e){
1983 infostream<<"WARNING: Could not load map metadata"
1984 //<<" Disabling chunk-based generator."
1989 infostream<<"ServerMap: Successfully loaded map "
1990 <<"metadata from "<<savedir
1991 <<", assuming valid save directory."
1992 <<" seed="<<m_seed<<"."
1995 m_map_saving_enabled = true;
1996 // Map loaded, not creating new one
2000 // If directory doesn't exist, it is safe to save to it
2002 m_map_saving_enabled = true;
2005 catch(std::exception &e)
2007 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2008 <<", exception: "<<e.what()<<std::endl;
2009 infostream<<"Please remove the map or fix it."<<std::endl;
2010 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2013 infostream<<"Initializing new map."<<std::endl;
2015 // Create zero sector
2016 emergeSector(v2s16(0,0));
2018 // Initially write whole map
2019 save(MOD_STATE_CLEAN);
2022 ServerMap::~ServerMap()
2024 verbosestream<<__FUNCTION_NAME<<std::endl;
2028 if(m_map_saving_enabled)
2030 // Save only changed parts
2031 save(MOD_STATE_WRITE_AT_UNLOAD);
2032 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2036 infostream<<"ServerMap: Map not saved"<<std::endl;
2039 catch(std::exception &e)
2041 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2042 <<", exception: "<<e.what()<<std::endl;
2046 Close database if it was opened
2049 sqlite3_finalize(m_database_read);
2050 if(m_database_write)
2051 sqlite3_finalize(m_database_write);
2053 sqlite3_close(m_database);
2059 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2060 for(; i.atEnd() == false; i++)
2062 MapChunk *chunk = i.getNode()->getValue();
2068 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2070 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2071 if(enable_mapgen_debug_info)
2072 infostream<<"initBlockMake(): "
2073 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2074 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2077 //s16 chunksize = 3;
2078 //v3s16 chunk_offset(-1,-1,-1);
2079 //s16 chunksize = 4;
2080 //v3s16 chunk_offset(-1,-1,-1);
2082 v3s16 chunk_offset(-2,-2,-2);
2083 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2084 v3s16 blockpos_min = blockpos_div * chunksize;
2085 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2086 blockpos_min += chunk_offset;
2087 blockpos_max += chunk_offset;
2089 //v3s16 extra_borders(1,1,1);
2090 v3s16 extra_borders(1,1,1);
2092 // Do nothing if not inside limits (+-1 because of neighbors)
2093 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2094 blockpos_over_limit(blockpos_max + extra_borders))
2100 data->no_op = false;
2101 data->seed = m_seed;
2102 data->blockpos_min = blockpos_min;
2103 data->blockpos_max = blockpos_max;
2104 data->blockpos_requested = blockpos;
2105 data->nodedef = m_gamedef->ndef();
2108 Create the whole area of this and the neighboring blocks
2111 //TimeTaker timer("initBlockMake() create area");
2113 for(s16 x=blockpos_min.X-extra_borders.X;
2114 x<=blockpos_max.X+extra_borders.X; x++)
2115 for(s16 z=blockpos_min.Z-extra_borders.Z;
2116 z<=blockpos_max.Z+extra_borders.Z; z++)
2118 v2s16 sectorpos(x, z);
2119 // Sector metadata is loaded from disk if not already loaded.
2120 ServerMapSector *sector = createSector(sectorpos);
2123 for(s16 y=blockpos_min.Y-extra_borders.Y;
2124 y<=blockpos_max.Y+extra_borders.Y; y++)
2127 //MapBlock *block = createBlock(p);
2128 // 1) get from memory, 2) load from disk
2129 MapBlock *block = emergeBlock(p, false);
2130 // 3) create a blank one
2133 block = createBlock(p);
2136 Block gets sunlight if this is true.
2138 Refer to the map generator heuristics.
2140 bool ug = mapgen::block_is_underground(data->seed, p);
2141 block->setIsUnderground(ug);
2144 // Lighting will not be valid after make_chunk is called
2145 block->setLightingExpired(true);
2146 // Lighting will be calculated
2147 //block->setLightingExpired(false);
2153 Now we have a big empty area.
2155 Make a ManualMapVoxelManipulator that contains this and the
2159 // The area that contains this block and it's neighbors
2160 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2161 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2163 data->vmanip = new ManualMapVoxelManipulator(this);
2164 //data->vmanip->setMap(this);
2168 //TimeTaker timer("initBlockMake() initialEmerge");
2169 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2172 // Data is ready now.
2175 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2176 core::map<v3s16, MapBlock*> &changed_blocks)
2178 v3s16 blockpos_min = data->blockpos_min;
2179 v3s16 blockpos_max = data->blockpos_max;
2180 v3s16 blockpos_requested = data->blockpos_requested;
2181 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2182 <<blockpos_requested.Y<<","
2183 <<blockpos_requested.Z<<")"<<std::endl;*/
2185 v3s16 extra_borders(1,1,1);
2189 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2193 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2195 /*infostream<<"Resulting vmanip:"<<std::endl;
2196 data->vmanip.print(infostream);*/
2198 // Make sure affected blocks are loaded
2199 for(s16 x=blockpos_min.X-extra_borders.X;
2200 x<=blockpos_max.X+extra_borders.X; x++)
2201 for(s16 z=blockpos_min.Z-extra_borders.Z;
2202 z<=blockpos_max.Z+extra_borders.Z; z++)
2203 for(s16 y=blockpos_min.Y-extra_borders.Y;
2204 y<=blockpos_max.Y+extra_borders.Y; y++)
2207 // Load from disk if not already in memory
2208 emergeBlock(p, false);
2212 Blit generated stuff to map
2213 NOTE: blitBackAll adds nearly everything to changed_blocks
2217 //TimeTaker timer("finishBlockMake() blitBackAll");
2218 data->vmanip->blitBackAll(&changed_blocks);
2221 if(enable_mapgen_debug_info)
2222 infostream<<"finishBlockMake: changed_blocks.size()="
2223 <<changed_blocks.size()<<std::endl;
2226 Copy transforming liquid information
2228 while(data->transforming_liquid.size() > 0)
2230 v3s16 p = data->transforming_liquid.pop_front();
2231 m_transforming_liquid.push_back(p);
2235 Do stuff in central blocks
2243 TimeTaker t("finishBlockMake lighting update");
2245 core::map<v3s16, MapBlock*> lighting_update_blocks;
2248 for(s16 x=blockpos_min.X-extra_borders.X;
2249 x<=blockpos_max.X+extra_borders.X; x++)
2250 for(s16 z=blockpos_min.Z-extra_borders.Z;
2251 z<=blockpos_max.Z+extra_borders.Z; z++)
2252 for(s16 y=blockpos_min.Y-extra_borders.Y;
2253 y<=blockpos_max.Y+extra_borders.Y; y++)
2256 MapBlock *block = getBlockNoCreateNoEx(p);
2258 lighting_update_blocks.insert(block->getPos(), block);
2261 updateLighting(lighting_update_blocks, changed_blocks);
2265 Set lighting to non-expired state in all of them.
2266 This is cheating, but it is not fast enough if all of them
2267 would actually be updated.
2269 for(s16 x=blockpos_min.X-extra_borders.X;
2270 x<=blockpos_max.X+extra_borders.X; x++)
2271 for(s16 z=blockpos_min.Z-extra_borders.Z;
2272 z<=blockpos_max.Z+extra_borders.Z; z++)
2273 for(s16 y=blockpos_min.Y-extra_borders.Y;
2274 y<=blockpos_max.Y+extra_borders.Y; y++)
2277 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2281 if(enable_mapgen_debug_info == false)
2282 t.stop(true); // Hide output
2287 Go through changed blocks
2289 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2290 i.atEnd() == false; i++)
2292 MapBlock *block = i.getNode()->getValue();
2295 Update day/night difference cache of the MapBlocks
2297 block->expireDayNightDiff();
2299 Set block as modified
2301 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2302 "finishBlockMake expireDayNightDiff");
2306 Set central blocks as generated
2308 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2309 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2310 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2313 MapBlock *block = getBlockNoCreateNoEx(p);
2315 block->setGenerated(true);
2319 Save changed parts of map
2320 NOTE: Will be saved later.
2322 //save(MOD_STATE_WRITE_AT_UNLOAD);
2324 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2325 <<","<<blockpos_requested.Y<<","
2326 <<blockpos_requested.Z<<")"<<std::endl;*/
2328 if(enable_mapgen_debug_info)
2331 Analyze resulting blocks
2333 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2334 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2335 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2336 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2337 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2338 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2340 v3s16 p = v3s16(x,y,z);
2341 MapBlock *block = getBlockNoCreateNoEx(p);
2343 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2344 infostream<<"Generated "<<spos<<": "
2345 <<analyze_block(block)<<std::endl;
2350 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2356 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2358 DSTACKF("%s: p2d=(%d,%d)",
2363 Check if it exists already in memory
2365 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2370 Try to load it from disk (with blocks)
2372 //if(loadSectorFull(p2d) == true)
2375 Try to load metadata from disk
2378 if(loadSectorMeta(p2d) == true)
2380 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2383 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2384 throw InvalidPositionException("");
2390 Do not create over-limit
2392 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2393 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2394 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2395 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2396 throw InvalidPositionException("createSector(): pos. over limit");
2399 Generate blank sector
2402 sector = new ServerMapSector(this, p2d, m_gamedef);
2404 // Sector position on map in nodes
2405 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2410 m_sectors.insert(p2d, sector);
2416 This is a quick-hand function for calling makeBlock().
2418 MapBlock * ServerMap::generateBlock(
2420 core::map<v3s16, MapBlock*> &modified_blocks
2423 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2425 /*infostream<<"generateBlock(): "
2426 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2429 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2431 TimeTaker timer("generateBlock");
2433 //MapBlock *block = original_dummy;
2435 v2s16 p2d(p.X, p.Z);
2436 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2439 Do not generate over-limit
2441 if(blockpos_over_limit(p))
2443 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2444 throw InvalidPositionException("generateBlock(): pos. over limit");
2448 Create block make data
2450 mapgen::BlockMakeData data;
2451 initBlockMake(&data, p);
2457 TimeTaker t("mapgen::make_block()");
2458 mapgen::make_block(&data);
2460 if(enable_mapgen_debug_info == false)
2461 t.stop(true); // Hide output
2465 Blit data back on map, update lighting, add mobs and whatever this does
2467 finishBlockMake(&data, modified_blocks);
2472 MapBlock *block = getBlockNoCreateNoEx(p);
2480 bool erroneus_content = false;
2481 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2482 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2483 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2486 MapNode n = block->getNode(p);
2487 if(n.getContent() == CONTENT_IGNORE)
2489 infostream<<"CONTENT_IGNORE at "
2490 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2492 erroneus_content = true;
2496 if(erroneus_content)
2505 Generate a completely empty block
2509 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2510 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2512 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2515 n.setContent(CONTENT_AIR);
2516 block->setNode(v3s16(x0,y0,z0), n);
2522 if(enable_mapgen_debug_info == false)
2523 timer.stop(true); // Hide output
2528 MapBlock * ServerMap::createBlock(v3s16 p)
2530 DSTACKF("%s: p=(%d,%d,%d)",
2531 __FUNCTION_NAME, p.X, p.Y, p.Z);
2534 Do not create over-limit
2536 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2537 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2538 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2539 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2540 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2541 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2542 throw InvalidPositionException("createBlock(): pos. over limit");
2544 v2s16 p2d(p.X, p.Z);
2547 This will create or load a sector if not found in memory.
2548 If block exists on disk, it will be loaded.
2550 NOTE: On old save formats, this will be slow, as it generates
2551 lighting on blocks for them.
2553 ServerMapSector *sector;
2555 sector = (ServerMapSector*)createSector(p2d);
2556 assert(sector->getId() == MAPSECTOR_SERVER);
2558 catch(InvalidPositionException &e)
2560 infostream<<"createBlock: createSector() failed"<<std::endl;
2564 NOTE: This should not be done, or at least the exception
2565 should not be passed on as std::exception, because it
2566 won't be catched at all.
2568 /*catch(std::exception &e)
2570 infostream<<"createBlock: createSector() failed: "
2571 <<e.what()<<std::endl;
2576 Try to get a block from the sector
2579 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2582 if(block->isDummy())
2587 block = sector->createBlankBlock(block_y);
2591 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2593 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2595 p.X, p.Y, p.Z, allow_generate);
2598 MapBlock *block = getBlockNoCreateNoEx(p);
2599 if(block && block->isDummy() == false)
2604 MapBlock *block = loadBlock(p);
2611 core::map<v3s16, MapBlock*> modified_blocks;
2612 MapBlock *block = generateBlock(p, modified_blocks);
2616 event.type = MEET_OTHER;
2619 // Copy modified_blocks to event
2620 for(core::map<v3s16, MapBlock*>::Iterator
2621 i = modified_blocks.getIterator();
2622 i.atEnd()==false; i++)
2624 event.modified_blocks.insert(i.getNode()->getKey(), false);
2628 dispatchEvent(&event);
2637 s16 ServerMap::findGroundLevel(v2s16 p2d)
2641 Uh, just do something random...
2643 // Find existing map from top to down
2646 v3s16 p(p2d.X, max, p2d.Y);
2647 for(; p.Y>min; p.Y--)
2649 MapNode n = getNodeNoEx(p);
2650 if(n.getContent() != CONTENT_IGNORE)
2655 // If this node is not air, go to plan b
2656 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2658 // Search existing walkable and return it
2659 for(; p.Y>min; p.Y--)
2661 MapNode n = getNodeNoEx(p);
2662 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2671 Determine from map generator noise functions
2674 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2677 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2678 //return (s16)level;
2681 void ServerMap::createDatabase() {
2684 e = sqlite3_exec(m_database,
2685 "CREATE TABLE IF NOT EXISTS `blocks` ("
2686 "`pos` INT NOT NULL PRIMARY KEY,"
2689 , NULL, NULL, NULL);
2690 if(e == SQLITE_ABORT)
2691 throw FileNotGoodException("Could not create database structure");
2693 infostream<<"ServerMap: Database structure was created";
2696 void ServerMap::verifyDatabase() {
2701 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2702 bool needs_create = false;
2706 Open the database connection
2709 createDirs(m_savedir);
2711 if(!fs::PathExists(dbp))
2712 needs_create = true;
2714 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2715 if(d != SQLITE_OK) {
2716 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2717 throw FileNotGoodException("Cannot open database file");
2723 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2724 if(d != SQLITE_OK) {
2725 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2726 throw FileNotGoodException("Cannot prepare read statement");
2729 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2730 if(d != SQLITE_OK) {
2731 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2732 throw FileNotGoodException("Cannot prepare write statement");
2735 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2736 if(d != SQLITE_OK) {
2737 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2738 throw FileNotGoodException("Cannot prepare read statement");
2741 infostream<<"ServerMap: Database opened"<<std::endl;
2745 bool ServerMap::loadFromFolders() {
2746 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2751 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2752 return (sqlite3_int64)pos.Z*16777216 +
2753 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2756 void ServerMap::createDirs(std::string path)
2758 if(fs::CreateAllDirs(path) == false)
2760 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2761 <<"\""<<path<<"\""<<std::endl;
2762 throw BaseException("ServerMap failed to create directory");
2766 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2772 snprintf(cc, 9, "%.4x%.4x",
2773 (unsigned int)pos.X&0xffff,
2774 (unsigned int)pos.Y&0xffff);
2776 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2778 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2779 (unsigned int)pos.X&0xfff,
2780 (unsigned int)pos.Y&0xfff);
2782 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2788 v2s16 ServerMap::getSectorPos(std::string dirname)
2792 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2793 assert(spos != std::string::npos);
2794 if(dirname.size() - spos == 8)
2797 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2799 else if(dirname.size() - spos == 3)
2802 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2803 // Sign-extend the 12 bit values up to 16 bits...
2804 if(x&0x800) x|=0xF000;
2805 if(y&0x800) y|=0xF000;
2812 v2s16 pos((s16)x, (s16)y);
2816 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2818 v2s16 p2d = getSectorPos(sectordir);
2820 if(blockfile.size() != 4){
2821 throw InvalidFilenameException("Invalid block filename");
2824 int r = sscanf(blockfile.c_str(), "%4x", &y);
2826 throw InvalidFilenameException("Invalid block filename");
2827 return v3s16(p2d.X, y, p2d.Y);
2830 std::string ServerMap::getBlockFilename(v3s16 p)
2833 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2837 void ServerMap::save(ModifiedState save_level)
2839 DSTACK(__FUNCTION_NAME);
2840 if(m_map_saving_enabled == false)
2842 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2846 if(save_level == MOD_STATE_CLEAN)
2847 infostream<<"ServerMap: Saving whole map, this can take time."
2850 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2855 // Profile modified reasons
2856 Profiler modprofiler;
2858 u32 sector_meta_count = 0;
2859 u32 block_count = 0;
2860 u32 block_count_all = 0; // Number of blocks in memory
2862 // Don't do anything with sqlite unless something is really saved
2863 bool save_started = false;
2865 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2866 for(; i.atEnd() == false; i++)
2868 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2869 assert(sector->getId() == MAPSECTOR_SERVER);
2871 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2873 saveSectorMeta(sector);
2874 sector_meta_count++;
2876 core::list<MapBlock*> blocks;
2877 sector->getBlocks(blocks);
2878 core::list<MapBlock*>::Iterator j;
2880 for(j=blocks.begin(); j!=blocks.end(); j++)
2882 MapBlock *block = *j;
2886 if(block->getModified() >= save_level)
2891 save_started = true;
2894 modprofiler.add(block->getModifiedReason(), 1);
2899 /*infostream<<"ServerMap: Written block ("
2900 <<block->getPos().X<<","
2901 <<block->getPos().Y<<","
2902 <<block->getPos().Z<<")"
2911 Only print if something happened or saved whole map
2913 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2914 || block_count != 0)
2916 infostream<<"ServerMap: Written: "
2917 <<sector_meta_count<<" sector metadata files, "
2918 <<block_count<<" block files"
2919 <<", "<<block_count_all<<" blocks in memory."
2921 PrintInfo(infostream); // ServerMap/ClientMap:
2922 infostream<<"Blocks modified by: "<<std::endl;
2923 modprofiler.print(infostream);
2927 static s32 unsignedToSigned(s32 i, s32 max_positive)
2929 if(i < max_positive)
2932 return i - 2*max_positive;
2935 // modulo of a negative number does not work consistently in C
2936 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2940 return mod - ((-i) % mod);
2943 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2945 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2947 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2949 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2950 return v3s16(x,y,z);
2953 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2955 if(loadFromFolders()){
2956 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2957 <<"all blocks that are stored in flat files"<<std::endl;
2963 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2965 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2966 v3s16 p = getIntegerAsBlock(block_i);
2967 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2973 void ServerMap::saveMapMeta()
2975 DSTACK(__FUNCTION_NAME);
2977 /*infostream<<"ServerMap::saveMapMeta(): "
2981 createDirs(m_savedir);
2983 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2984 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2985 if(os.good() == false)
2987 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2988 <<"could not open"<<fullpath<<std::endl;
2989 throw FileNotGoodException("Cannot open chunk metadata");
2993 params.setU64("seed", m_seed);
2995 params.writeLines(os);
2997 os<<"[end_of_params]\n";
2999 m_map_metadata_changed = false;
3002 void ServerMap::loadMapMeta()
3004 DSTACK(__FUNCTION_NAME);
3006 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3009 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3010 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3011 if(is.good() == false)
3013 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3014 <<"could not open"<<fullpath<<std::endl;
3015 throw FileNotGoodException("Cannot open map metadata");
3023 throw SerializationError
3024 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3026 std::getline(is, line);
3027 std::string trimmedline = trim(line);
3028 if(trimmedline == "[end_of_params]")
3030 params.parseConfigLine(line);
3033 m_seed = params.getU64("seed");
3035 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3038 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3040 DSTACK(__FUNCTION_NAME);
3041 // Format used for writing
3042 u8 version = SER_FMT_VER_HIGHEST;
3044 v2s16 pos = sector->getPos();
3045 std::string dir = getSectorDir(pos);
3048 std::string fullpath = dir + DIR_DELIM + "meta";
3049 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3050 if(o.good() == false)
3051 throw FileNotGoodException("Cannot open sector metafile");
3053 sector->serialize(o, version);
3055 sector->differs_from_disk = false;
3058 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3060 DSTACK(__FUNCTION_NAME);
3062 v2s16 p2d = getSectorPos(sectordir);
3064 ServerMapSector *sector = NULL;
3066 std::string fullpath = sectordir + DIR_DELIM + "meta";
3067 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3068 if(is.good() == false)
3070 // If the directory exists anyway, it probably is in some old
3071 // format. Just go ahead and create the sector.
3072 if(fs::PathExists(sectordir))
3074 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3075 <<fullpath<<" doesn't exist but directory does."
3076 <<" Continuing with a sector with no metadata."
3078 sector = new ServerMapSector(this, p2d, m_gamedef);
3079 m_sectors.insert(p2d, sector);
3083 throw FileNotGoodException("Cannot open sector metafile");
3088 sector = ServerMapSector::deSerialize
3089 (is, this, p2d, m_sectors, m_gamedef);
3091 saveSectorMeta(sector);
3094 sector->differs_from_disk = false;
3099 bool ServerMap::loadSectorMeta(v2s16 p2d)
3101 DSTACK(__FUNCTION_NAME);
3103 MapSector *sector = NULL;
3105 // The directory layout we're going to load from.
3106 // 1 - original sectors/xxxxzzzz/
3107 // 2 - new sectors2/xxx/zzz/
3108 // If we load from anything but the latest structure, we will
3109 // immediately save to the new one, and remove the old.
3111 std::string sectordir1 = getSectorDir(p2d, 1);
3112 std::string sectordir;
3113 if(fs::PathExists(sectordir1))
3115 sectordir = sectordir1;
3120 sectordir = getSectorDir(p2d, 2);
3124 sector = loadSectorMeta(sectordir, loadlayout != 2);
3126 catch(InvalidFilenameException &e)
3130 catch(FileNotGoodException &e)
3134 catch(std::exception &e)
3143 bool ServerMap::loadSectorFull(v2s16 p2d)
3145 DSTACK(__FUNCTION_NAME);
3147 MapSector *sector = NULL;
3149 // The directory layout we're going to load from.
3150 // 1 - original sectors/xxxxzzzz/
3151 // 2 - new sectors2/xxx/zzz/
3152 // If we load from anything but the latest structure, we will
3153 // immediately save to the new one, and remove the old.
3155 std::string sectordir1 = getSectorDir(p2d, 1);
3156 std::string sectordir;
3157 if(fs::PathExists(sectordir1))
3159 sectordir = sectordir1;
3164 sectordir = getSectorDir(p2d, 2);
3168 sector = loadSectorMeta(sectordir, loadlayout != 2);
3170 catch(InvalidFilenameException &e)
3174 catch(FileNotGoodException &e)
3178 catch(std::exception &e)
3186 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3188 std::vector<fs::DirListNode>::iterator i2;
3189 for(i2=list2.begin(); i2!=list2.end(); i2++)
3195 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3197 catch(InvalidFilenameException &e)
3199 // This catches unknown crap in directory
3205 infostream<<"Sector converted to new layout - deleting "<<
3206 sectordir1<<std::endl;
3207 fs::RecursiveDelete(sectordir1);
3214 void ServerMap::beginSave() {
3216 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3217 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3220 void ServerMap::endSave() {
3222 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3223 infostream<<"WARNING: endSave() failed, map might not have saved.";
3226 void ServerMap::saveBlock(MapBlock *block)
3228 DSTACK(__FUNCTION_NAME);
3230 Dummy blocks are not written
3232 if(block->isDummy())
3234 /*v3s16 p = block->getPos();
3235 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3236 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3240 // Format used for writing
3241 u8 version = SER_FMT_VER_HIGHEST;
3243 v3s16 p3d = block->getPos();
3247 v2s16 p2d(p3d.X, p3d.Z);
3248 std::string sectordir = getSectorDir(p2d);
3250 createDirs(sectordir);
3252 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3253 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3254 if(o.good() == false)
3255 throw FileNotGoodException("Cannot open block data");
3258 [0] u8 serialization version
3264 std::ostringstream o(std::ios_base::binary);
3266 o.write((char*)&version, 1);
3269 block->serialize(o, version, true);
3271 // Write block to database
3273 std::string tmp = o.str();
3274 const char *bytes = tmp.c_str();
3276 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3277 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3278 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3279 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3280 int written = sqlite3_step(m_database_write);
3281 if(written != SQLITE_DONE)
3282 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3283 <<sqlite3_errmsg(m_database)<<std::endl;
3284 // Make ready for later reuse
3285 sqlite3_reset(m_database_write);
3287 // We just wrote it to the disk so clear modified flag
3288 block->resetModified();
3291 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3293 DSTACK(__FUNCTION_NAME);
3295 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3298 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3299 if(is.good() == false)
3300 throw FileNotGoodException("Cannot open block file");
3302 v3s16 p3d = getBlockPos(sectordir, blockfile);
3303 v2s16 p2d(p3d.X, p3d.Z);
3305 assert(sector->getPos() == p2d);
3307 u8 version = SER_FMT_VER_INVALID;
3308 is.read((char*)&version, 1);
3311 throw SerializationError("ServerMap::loadBlock(): Failed"
3312 " to read MapBlock version");
3314 /*u32 block_size = MapBlock::serializedLength(version);
3315 SharedBuffer<u8> data(block_size);
3316 is.read((char*)*data, block_size);*/
3318 // This will always return a sector because we're the server
3319 //MapSector *sector = emergeSector(p2d);
3321 MapBlock *block = NULL;
3322 bool created_new = false;
3323 block = sector->getBlockNoCreateNoEx(p3d.Y);
3326 block = sector->createBlankBlockNoInsert(p3d.Y);
3331 block->deSerialize(is, version, true);
3333 // If it's a new block, insert it to the map
3335 sector->insertBlock(block);
3338 Save blocks loaded in old format in new format
3341 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3345 // Should be in database now, so delete the old file
3346 fs::RecursiveDelete(fullpath);
3349 // We just loaded it from the disk, so it's up-to-date.
3350 block->resetModified();
3353 catch(SerializationError &e)
3355 infostream<<"WARNING: Invalid block data on disk "
3356 <<"fullpath="<<fullpath
3357 <<" (SerializationError). "
3358 <<"what()="<<e.what()
3360 //" Ignoring. A new one will be generated.
3363 // TODO: Backup file; name is in fullpath.
3367 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3369 DSTACK(__FUNCTION_NAME);
3372 std::istringstream is(*blob, std::ios_base::binary);
3374 u8 version = SER_FMT_VER_INVALID;
3375 is.read((char*)&version, 1);
3378 throw SerializationError("ServerMap::loadBlock(): Failed"
3379 " to read MapBlock version");
3381 /*u32 block_size = MapBlock::serializedLength(version);
3382 SharedBuffer<u8> data(block_size);
3383 is.read((char*)*data, block_size);*/
3385 // This will always return a sector because we're the server
3386 //MapSector *sector = emergeSector(p2d);
3388 MapBlock *block = NULL;
3389 bool created_new = false;
3390 block = sector->getBlockNoCreateNoEx(p3d.Y);
3393 block = sector->createBlankBlockNoInsert(p3d.Y);
3398 block->deSerialize(is, version, true);
3400 // If it's a new block, insert it to the map
3402 sector->insertBlock(block);
3405 Save blocks loaded in old format in new format
3408 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3409 // Only save if asked to; no need to update version
3413 // We just loaded it from, so it's up-to-date.
3414 block->resetModified();
3417 catch(SerializationError &e)
3419 infostream<<"WARNING: Invalid block data in database "
3420 <<" (SerializationError). "
3421 <<"what()="<<e.what()
3423 //" Ignoring. A new one will be generated.
3426 // TODO: Copy to a backup database.
3430 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3432 DSTACK(__FUNCTION_NAME);
3434 v2s16 p2d(blockpos.X, blockpos.Z);
3436 if(!loadFromFolders()) {
3439 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3440 infostream<<"WARNING: Could not bind block position for load: "
3441 <<sqlite3_errmsg(m_database)<<std::endl;
3442 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3444 Make sure sector is loaded
3446 MapSector *sector = createSector(p2d);
3451 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3452 size_t len = sqlite3_column_bytes(m_database_read, 0);
3454 std::string datastr(data, len);
3456 loadBlock(&datastr, blockpos, sector, false);
3458 sqlite3_step(m_database_read);
3459 // We should never get more than 1 row, so ok to reset
3460 sqlite3_reset(m_database_read);
3462 return getBlockNoCreateNoEx(blockpos);
3464 sqlite3_reset(m_database_read);
3466 // Not found in database, try the files
3469 // The directory layout we're going to load from.
3470 // 1 - original sectors/xxxxzzzz/
3471 // 2 - new sectors2/xxx/zzz/
3472 // If we load from anything but the latest structure, we will
3473 // immediately save to the new one, and remove the old.
3475 std::string sectordir1 = getSectorDir(p2d, 1);
3476 std::string sectordir;
3477 if(fs::PathExists(sectordir1))
3479 sectordir = sectordir1;
3484 sectordir = getSectorDir(p2d, 2);
3488 Make sure sector is loaded
3490 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3494 sector = loadSectorMeta(sectordir, loadlayout != 2);
3496 catch(InvalidFilenameException &e)
3500 catch(FileNotGoodException &e)
3504 catch(std::exception &e)
3511 Make sure file exists
3514 std::string blockfilename = getBlockFilename(blockpos);
3515 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3519 Load block and save it to the database
3521 loadBlock(sectordir, blockfilename, sector, true);
3522 return getBlockNoCreateNoEx(blockpos);
3525 void ServerMap::PrintInfo(std::ostream &out)
3534 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3539 MapVoxelManipulator::~MapVoxelManipulator()
3541 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3545 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3547 TimeTaker timer1("emerge", &emerge_time);
3549 // Units of these are MapBlocks
3550 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3551 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3553 VoxelArea block_area_nodes
3554 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3556 addArea(block_area_nodes);
3558 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3559 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3560 for(s32 x=p_min.X; x<=p_max.X; x++)
3563 core::map<v3s16, bool>::Node *n;
3564 n = m_loaded_blocks.find(p);
3568 bool block_data_inexistent = false;
3571 TimeTaker timer1("emerge load", &emerge_load_time);
3573 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3574 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3576 a.print(infostream);
3577 infostream<<std::endl;*/
3579 MapBlock *block = m_map->getBlockNoCreate(p);
3580 if(block->isDummy())
3581 block_data_inexistent = true;
3583 block->copyTo(*this);
3585 catch(InvalidPositionException &e)
3587 block_data_inexistent = true;
3590 if(block_data_inexistent)
3592 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3593 // Fill with VOXELFLAG_INEXISTENT
3594 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3595 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3597 s32 i = m_area.index(a.MinEdge.X,y,z);
3598 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3602 m_loaded_blocks.insert(p, !block_data_inexistent);
3605 //infostream<<"emerge done"<<std::endl;
3609 SUGG: Add an option to only update eg. water and air nodes.
3610 This will make it interfere less with important stuff if
3613 void MapVoxelManipulator::blitBack
3614 (core::map<v3s16, MapBlock*> & modified_blocks)
3616 if(m_area.getExtent() == v3s16(0,0,0))
3619 //TimeTaker timer1("blitBack");
3621 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3622 <<m_loaded_blocks.size()<<std::endl;*/
3625 Initialize block cache
3627 v3s16 blockpos_last;
3628 MapBlock *block = NULL;
3629 bool block_checked_in_modified = false;
3631 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3632 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3633 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3637 u8 f = m_flags[m_area.index(p)];
3638 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3641 MapNode &n = m_data[m_area.index(p)];
3643 v3s16 blockpos = getNodeBlockPos(p);
3648 if(block == NULL || blockpos != blockpos_last){
3649 block = m_map->getBlockNoCreate(blockpos);
3650 blockpos_last = blockpos;
3651 block_checked_in_modified = false;
3654 // Calculate relative position in block
3655 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3657 // Don't continue if nothing has changed here
3658 if(block->getNode(relpos) == n)
3661 //m_map->setNode(m_area.MinEdge + p, n);
3662 block->setNode(relpos, n);
3665 Make sure block is in modified_blocks
3667 if(block_checked_in_modified == false)
3669 modified_blocks[blockpos] = block;
3670 block_checked_in_modified = true;
3673 catch(InvalidPositionException &e)
3679 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3680 MapVoxelManipulator(map),
3681 m_create_area(false)
3685 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3689 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3691 // Just create the area so that it can be pointed to
3692 VoxelManipulator::emerge(a, caller_id);
3695 void ManualMapVoxelManipulator::initialEmerge(
3696 v3s16 blockpos_min, v3s16 blockpos_max)
3698 TimeTaker timer1("initialEmerge", &emerge_time);
3700 // Units of these are MapBlocks
3701 v3s16 p_min = blockpos_min;
3702 v3s16 p_max = blockpos_max;
3704 VoxelArea block_area_nodes
3705 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3707 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3710 infostream<<"initialEmerge: area: ";
3711 block_area_nodes.print(infostream);
3712 infostream<<" ("<<size_MB<<"MB)";
3713 infostream<<std::endl;
3716 addArea(block_area_nodes);
3718 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3719 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3720 for(s32 x=p_min.X; x<=p_max.X; x++)
3723 core::map<v3s16, bool>::Node *n;
3724 n = m_loaded_blocks.find(p);
3728 bool block_data_inexistent = false;
3731 TimeTaker timer1("emerge load", &emerge_load_time);
3733 MapBlock *block = m_map->getBlockNoCreate(p);
3734 if(block->isDummy())
3735 block_data_inexistent = true;
3737 block->copyTo(*this);
3739 catch(InvalidPositionException &e)
3741 block_data_inexistent = true;
3744 if(block_data_inexistent)
3747 Mark area inexistent
3749 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3750 // Fill with VOXELFLAG_INEXISTENT
3751 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3752 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3754 s32 i = m_area.index(a.MinEdge.X,y,z);
3755 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3759 m_loaded_blocks.insert(p, !block_data_inexistent);
3763 void ManualMapVoxelManipulator::blitBackAll(
3764 core::map<v3s16, MapBlock*> * modified_blocks)
3766 if(m_area.getExtent() == v3s16(0,0,0))
3770 Copy data of all blocks
3772 for(core::map<v3s16, bool>::Iterator
3773 i = m_loaded_blocks.getIterator();
3774 i.atEnd() == false; i++)
3776 v3s16 p = i.getNode()->getKey();
3777 bool existed = i.getNode()->getValue();
3778 if(existed == false)
3780 // The Great Bug was found using this
3781 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3782 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3786 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3789 infostream<<"WARNING: "<<__FUNCTION_NAME
3790 <<": got NULL block "
3791 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3796 block->copyFrom(*this);
3799 modified_blocks->insert(p, block);