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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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"
28 #include "nodemetadata.h"
34 #include "util/directiontables.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 Remove node metadata
1016 removeNodeMetadata(p);
1019 Set the node on the map
1025 If node is under sunlight and doesn't let sunlight through,
1026 take all sunlighted nodes under it and clear light from them
1027 and from where the light has been spread.
1028 TODO: This could be optimized by mass-unlighting instead
1031 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1035 //m_dout<<DTIME<<"y="<<y<<std::endl;
1036 v3s16 n2pos(p.X, y, p.Z);
1040 n2 = getNode(n2pos);
1042 catch(InvalidPositionException &e)
1047 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1049 unLightNeighbors(LIGHTBANK_DAY,
1050 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1051 light_sources, modified_blocks);
1052 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1060 for(s32 i=0; i<2; i++)
1062 enum LightBank bank = banks[i];
1065 Spread light from all nodes that might be capable of doing so
1067 spreadLight(bank, light_sources, modified_blocks);
1071 Update information about whether day and night light differ
1073 for(core::map<v3s16, MapBlock*>::Iterator
1074 i = modified_blocks.getIterator();
1075 i.atEnd() == false; i++)
1077 MapBlock *block = i.getNode()->getValue();
1078 block->expireDayNightDiff();
1082 Add neighboring liquid nodes and the node itself if it is
1083 liquid (=water node was added) to transform queue.
1086 v3s16(0,0,0), // self
1087 v3s16(0,0,1), // back
1088 v3s16(0,1,0), // top
1089 v3s16(1,0,0), // right
1090 v3s16(0,0,-1), // front
1091 v3s16(0,-1,0), // bottom
1092 v3s16(-1,0,0), // left
1094 for(u16 i=0; i<7; i++)
1099 v3s16 p2 = p + dirs[i];
1101 MapNode n2 = getNode(p2);
1102 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1104 m_transforming_liquid.push_back(p2);
1107 }catch(InvalidPositionException &e)
1115 void Map::removeNodeAndUpdate(v3s16 p,
1116 core::map<v3s16, MapBlock*> &modified_blocks)
1118 INodeDefManager *nodemgr = m_gamedef->ndef();
1120 /*PrintInfo(m_dout);
1121 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1122 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1124 bool node_under_sunlight = true;
1126 v3s16 toppos = p + v3s16(0,1,0);
1128 // Node will be replaced with this
1129 content_t replace_material = CONTENT_AIR;
1132 If there is a node at top and it doesn't have sunlight,
1133 there will be no sunlight going down.
1136 MapNode topnode = getNode(toppos);
1138 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1139 node_under_sunlight = false;
1141 catch(InvalidPositionException &e)
1145 core::map<v3s16, bool> light_sources;
1147 enum LightBank banks[] =
1152 for(s32 i=0; i<2; i++)
1154 enum LightBank bank = banks[i];
1157 Unlight neighbors (in case the node is a light source)
1159 unLightNeighbors(bank, p,
1160 getNode(p).getLight(bank, nodemgr),
1161 light_sources, modified_blocks);
1165 Remove node metadata
1168 removeNodeMetadata(p);
1172 This also clears the lighting.
1176 n.setContent(replace_material);
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1184 Recalculate lighting
1186 spreadLight(bank, light_sources, modified_blocks);
1189 // Add the block of the removed node to modified_blocks
1190 v3s16 blockpos = getNodeBlockPos(p);
1191 MapBlock * block = getBlockNoCreate(blockpos);
1192 assert(block != NULL);
1193 modified_blocks.insert(blockpos, block);
1196 If the removed node was under sunlight, propagate the
1197 sunlight down from it and then light all neighbors
1198 of the propagated blocks.
1200 if(node_under_sunlight)
1202 s16 ybottom = propagateSunlight(p, modified_blocks);
1203 /*m_dout<<DTIME<<"Node was under sunlight. "
1204 "Propagating sunlight";
1205 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1207 for(; y >= ybottom; y--)
1209 v3s16 p2(p.X, y, p.Z);
1210 /*m_dout<<DTIME<<"lighting neighbors of node ("
1211 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1213 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1218 // Set the lighting of this node to 0
1219 // TODO: Is this needed? Lighting is cleared up there already.
1221 MapNode n = getNode(p);
1222 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1225 catch(InvalidPositionException &e)
1231 for(s32 i=0; i<2; i++)
1233 enum LightBank bank = banks[i];
1235 // Get the brightest neighbour node and propagate light from it
1236 v3s16 n2p = getBrightestNeighbour(bank, p);
1238 MapNode n2 = getNode(n2p);
1239 lightNeighbors(bank, n2p, modified_blocks);
1241 catch(InvalidPositionException &e)
1247 Update information about whether day and night light differ
1249 for(core::map<v3s16, MapBlock*>::Iterator
1250 i = modified_blocks.getIterator();
1251 i.atEnd() == false; i++)
1253 MapBlock *block = i.getNode()->getValue();
1254 block->expireDayNightDiff();
1258 Add neighboring liquid nodes and this node to transform queue.
1259 (it's vital for the node itself to get updated last.)
1262 v3s16(0,0,1), // back
1263 v3s16(0,1,0), // top
1264 v3s16(1,0,0), // right
1265 v3s16(0,0,-1), // front
1266 v3s16(0,-1,0), // bottom
1267 v3s16(-1,0,0), // left
1268 v3s16(0,0,0), // self
1270 for(u16 i=0; i<7; i++)
1275 v3s16 p2 = p + dirs[i];
1277 MapNode n2 = getNode(p2);
1278 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1280 m_transforming_liquid.push_back(p2);
1283 }catch(InvalidPositionException &e)
1289 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1292 event.type = MEET_ADDNODE;
1296 bool succeeded = true;
1298 core::map<v3s16, MapBlock*> modified_blocks;
1299 addNodeAndUpdate(p, n, modified_blocks);
1301 // Copy modified_blocks to event
1302 for(core::map<v3s16, MapBlock*>::Iterator
1303 i = modified_blocks.getIterator();
1304 i.atEnd()==false; i++)
1306 event.modified_blocks.insert(i.getNode()->getKey(), false);
1309 catch(InvalidPositionException &e){
1313 dispatchEvent(&event);
1318 bool Map::removeNodeWithEvent(v3s16 p)
1321 event.type = MEET_REMOVENODE;
1324 bool succeeded = true;
1326 core::map<v3s16, MapBlock*> modified_blocks;
1327 removeNodeAndUpdate(p, modified_blocks);
1329 // Copy modified_blocks to event
1330 for(core::map<v3s16, MapBlock*>::Iterator
1331 i = modified_blocks.getIterator();
1332 i.atEnd()==false; i++)
1334 event.modified_blocks.insert(i.getNode()->getKey(), false);
1337 catch(InvalidPositionException &e){
1341 dispatchEvent(&event);
1346 bool Map::getDayNightDiff(v3s16 blockpos)
1349 v3s16 p = blockpos + v3s16(0,0,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->getDayNightDiff())
1354 catch(InvalidPositionException &e){}
1357 v3s16 p = blockpos + v3s16(-1,0,0);
1358 MapBlock *b = getBlockNoCreate(p);
1359 if(b->getDayNightDiff())
1362 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(0,-1,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->getDayNightDiff())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,0,-1);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->getDayNightDiff())
1376 catch(InvalidPositionException &e){}
1379 v3s16 p = blockpos + v3s16(1,0,0);
1380 MapBlock *b = getBlockNoCreate(p);
1381 if(b->getDayNightDiff())
1384 catch(InvalidPositionException &e){}
1386 v3s16 p = blockpos + v3s16(0,1,0);
1387 MapBlock *b = getBlockNoCreate(p);
1388 if(b->getDayNightDiff())
1391 catch(InvalidPositionException &e){}
1393 v3s16 p = blockpos + v3s16(0,0,1);
1394 MapBlock *b = getBlockNoCreate(p);
1395 if(b->getDayNightDiff())
1398 catch(InvalidPositionException &e){}
1404 Updates usage timers
1406 void Map::timerUpdate(float dtime, float unload_timeout,
1407 core::list<v3s16> *unloaded_blocks)
1409 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1411 // Profile modified reasons
1412 Profiler modprofiler;
1414 core::list<v2s16> sector_deletion_queue;
1415 u32 deleted_blocks_count = 0;
1416 u32 saved_blocks_count = 0;
1417 u32 block_count_all = 0;
1419 core::map<v2s16, MapSector*>::Iterator si;
1422 si = m_sectors.getIterator();
1423 for(; si.atEnd() == false; si++)
1425 MapSector *sector = si.getNode()->getValue();
1427 bool all_blocks_deleted = true;
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1432 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1433 i != blocks.end(); i++)
1435 MapBlock *block = (*i);
1437 block->incrementUsageTimer(dtime);
1439 if(block->getUsageTimer() > unload_timeout)
1441 v3s16 p = block->getPos();
1444 if(block->getModified() != MOD_STATE_CLEAN
1445 && save_before_unloading)
1447 modprofiler.add(block->getModifiedReason(), 1);
1449 saved_blocks_count++;
1452 // Delete from memory
1453 sector->deleteBlock(block);
1456 unloaded_blocks->push_back(p);
1458 deleted_blocks_count++;
1462 all_blocks_deleted = false;
1467 if(all_blocks_deleted)
1469 sector_deletion_queue.push_back(si.getNode()->getKey());
1474 // Finally delete the empty sectors
1475 deleteSectors(sector_deletion_queue);
1477 if(deleted_blocks_count != 0)
1479 PrintInfo(infostream); // ServerMap/ClientMap:
1480 infostream<<"Unloaded "<<deleted_blocks_count
1481 <<" blocks from memory";
1482 if(save_before_unloading)
1483 infostream<<", of which "<<saved_blocks_count<<" were written";
1484 infostream<<", "<<block_count_all<<" blocks in memory";
1485 infostream<<"."<<std::endl;
1486 if(saved_blocks_count != 0){
1487 PrintInfo(infostream); // ServerMap/ClientMap:
1488 infostream<<"Blocks modified by: "<<std::endl;
1489 modprofiler.print(infostream);
1494 void Map::deleteSectors(core::list<v2s16> &list)
1496 core::list<v2s16>::Iterator j;
1497 for(j=list.begin(); j!=list.end(); j++)
1499 MapSector *sector = m_sectors[*j];
1500 // If sector is in sector cache, remove it from there
1501 if(m_sector_cache == sector)
1502 m_sector_cache = NULL;
1503 // Remove from map and delete
1504 m_sectors.remove(*j);
1510 void Map::unloadUnusedData(float timeout,
1511 core::list<v3s16> *deleted_blocks)
1513 core::list<v2s16> sector_deletion_queue;
1514 u32 deleted_blocks_count = 0;
1515 u32 saved_blocks_count = 0;
1517 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1518 for(; si.atEnd() == false; si++)
1520 MapSector *sector = si.getNode()->getValue();
1522 bool all_blocks_deleted = true;
1524 core::list<MapBlock*> blocks;
1525 sector->getBlocks(blocks);
1526 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1527 i != blocks.end(); i++)
1529 MapBlock *block = (*i);
1531 if(block->getUsageTimer() > timeout)
1534 if(block->getModified() != MOD_STATE_CLEAN)
1537 saved_blocks_count++;
1539 // Delete from memory
1540 sector->deleteBlock(block);
1541 deleted_blocks_count++;
1545 all_blocks_deleted = false;
1549 if(all_blocks_deleted)
1551 sector_deletion_queue.push_back(si.getNode()->getKey());
1555 deleteSectors(sector_deletion_queue);
1557 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1558 <<", of which "<<saved_blocks_count<<" were wr."
1561 //return sector_deletion_queue.getSize();
1562 //return deleted_blocks_count;
1566 void Map::PrintInfo(std::ostream &out)
1571 #define WATER_DROP_BOOST 4
1575 NEIGHBOR_SAME_LEVEL,
1578 struct NodeNeighbor {
1584 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1586 INodeDefManager *nodemgr = m_gamedef->ndef();
1588 DSTACK(__FUNCTION_NAME);
1589 //TimeTaker timer("transformLiquids()");
1592 u32 initial_size = m_transforming_liquid.size();
1594 /*if(initial_size != 0)
1595 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1597 // list of nodes that due to viscosity have not reached their max level height
1598 UniqueQueue<v3s16> must_reflow;
1600 // List of MapBlocks that will require a lighting update (due to lava)
1601 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1603 while(m_transforming_liquid.size() != 0)
1605 // This should be done here so that it is done when continue is used
1606 if(loopcount >= initial_size * 3)
1611 Get a queued transforming liquid node
1613 v3s16 p0 = m_transforming_liquid.pop_front();
1615 MapNode n0 = getNodeNoEx(p0);
1618 Collect information about current node
1620 s8 liquid_level = -1;
1621 u8 liquid_kind = CONTENT_IGNORE;
1622 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1623 switch (liquid_type) {
1625 liquid_level = LIQUID_LEVEL_SOURCE;
1626 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1628 case LIQUID_FLOWING:
1629 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1630 liquid_kind = n0.getContent();
1633 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1634 // continue with the next node.
1635 if (n0.getContent() != CONTENT_AIR)
1637 liquid_kind = CONTENT_AIR;
1642 Collect information about the environment
1644 const v3s16 *dirs = g_6dirs;
1645 NodeNeighbor sources[6]; // surrounding sources
1646 int num_sources = 0;
1647 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1649 NodeNeighbor airs[6]; // surrounding air
1651 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1652 int num_neutrals = 0;
1653 bool flowing_down = false;
1654 for (u16 i = 0; i < 6; i++) {
1655 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1658 nt = NEIGHBOR_UPPER;
1661 nt = NEIGHBOR_LOWER;
1664 v3s16 npos = p0 + dirs[i];
1665 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1666 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1668 if (nb.n.getContent() == CONTENT_AIR) {
1669 airs[num_airs++] = nb;
1670 // if the current node is a water source the neighbor
1671 // should be enqueded for transformation regardless of whether the
1672 // current node changes or not.
1673 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1674 m_transforming_liquid.push_back(npos);
1675 // if the current node happens to be a flowing node, it will start to flow down here.
1676 if (nb.t == NEIGHBOR_LOWER) {
1677 flowing_down = true;
1680 neutrals[num_neutrals++] = nb;
1684 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1685 if (liquid_kind == CONTENT_AIR)
1686 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1687 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1688 neutrals[num_neutrals++] = nb;
1690 // Do not count bottom source, it will screw things up
1692 sources[num_sources++] = nb;
1695 case LIQUID_FLOWING:
1696 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1697 if (liquid_kind == CONTENT_AIR)
1698 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1699 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1700 neutrals[num_neutrals++] = nb;
1702 flows[num_flows++] = nb;
1703 if (nb.t == NEIGHBOR_LOWER)
1704 flowing_down = true;
1711 decide on the type (and possibly level) of the current node
1713 content_t new_node_content;
1714 s8 new_node_level = -1;
1715 s8 max_node_level = -1;
1716 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1717 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1718 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1719 // it's perfectly safe to use liquid_kind here to determine the new node content.
1720 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1721 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1722 // liquid_kind is set properly, see above
1723 new_node_content = liquid_kind;
1724 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1726 // no surrounding sources, so get the maximum level that can flow into this node
1727 for (u16 i = 0; i < num_flows; i++) {
1728 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1729 switch (flows[i].t) {
1730 case NEIGHBOR_UPPER:
1731 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1732 max_node_level = LIQUID_LEVEL_MAX;
1733 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1734 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1735 } else if (nb_liquid_level > max_node_level)
1736 max_node_level = nb_liquid_level;
1738 case NEIGHBOR_LOWER:
1740 case NEIGHBOR_SAME_LEVEL:
1741 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1742 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1743 max_node_level = nb_liquid_level - 1;
1749 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1750 if (viscosity > 1 && max_node_level != liquid_level) {
1751 // amount to gain, limited by viscosity
1752 // must be at least 1 in absolute value
1753 s8 level_inc = max_node_level - liquid_level;
1754 if (level_inc < -viscosity || level_inc > viscosity)
1755 new_node_level = liquid_level + level_inc/viscosity;
1756 else if (level_inc < 0)
1757 new_node_level = liquid_level - 1;
1758 else if (level_inc > 0)
1759 new_node_level = liquid_level + 1;
1760 if (new_node_level != max_node_level)
1761 must_reflow.push_back(p0);
1763 new_node_level = max_node_level;
1765 if (new_node_level >= 0)
1766 new_node_content = liquid_kind;
1768 new_node_content = CONTENT_AIR;
1773 check if anything has changed. if not, just continue with the next node.
1775 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1776 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1777 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1783 update the current node
1785 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1786 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1787 // set level to last 3 bits, flowing down bit to 4th bit
1788 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1790 // set the liquid level and flow bit to 0
1791 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1793 n0.setContent(new_node_content);
1795 v3s16 blockpos = getNodeBlockPos(p0);
1796 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1798 modified_blocks.insert(blockpos, block);
1799 // If node emits light, MapBlock requires lighting update
1800 if(nodemgr->get(n0).light_source != 0)
1801 lighting_modified_blocks[block->getPos()] = block;
1805 enqueue neighbors for update if neccessary
1807 switch (nodemgr->get(n0.getContent()).liquid_type) {
1809 case LIQUID_FLOWING:
1810 // make sure source flows into all neighboring nodes
1811 for (u16 i = 0; i < num_flows; i++)
1812 if (flows[i].t != NEIGHBOR_UPPER)
1813 m_transforming_liquid.push_back(flows[i].p);
1814 for (u16 i = 0; i < num_airs; i++)
1815 if (airs[i].t != NEIGHBOR_UPPER)
1816 m_transforming_liquid.push_back(airs[i].p);
1819 // this flow has turned to air; neighboring flows might need to do the same
1820 for (u16 i = 0; i < num_flows; i++)
1821 m_transforming_liquid.push_back(flows[i].p);
1825 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1826 while (must_reflow.size() > 0)
1827 m_transforming_liquid.push_back(must_reflow.pop_front());
1828 updateLighting(lighting_modified_blocks, modified_blocks);
1831 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1833 v3s16 blockpos = getNodeBlockPos(p);
1834 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1835 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1837 infostream<<"Map::getNodeMetadata(): Need to emerge "
1838 <<PP(blockpos)<<std::endl;
1839 block = emergeBlock(blockpos, false);
1843 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1847 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1851 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1853 v3s16 blockpos = getNodeBlockPos(p);
1854 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1855 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1857 infostream<<"Map::setNodeMetadata(): Need to emerge "
1858 <<PP(blockpos)<<std::endl;
1859 block = emergeBlock(blockpos, false);
1863 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1867 block->m_node_metadata.set(p_rel, meta);
1870 void Map::removeNodeMetadata(v3s16 p)
1872 v3s16 blockpos = getNodeBlockPos(p);
1873 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1874 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1877 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1881 block->m_node_metadata.remove(p_rel);
1888 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1889 Map(dout_server, gamedef),
1891 m_map_metadata_changed(true),
1893 m_database_read(NULL),
1894 m_database_write(NULL)
1896 verbosestream<<__FUNCTION_NAME<<std::endl;
1898 //m_chunksize = 8; // Takes a few seconds
1900 if (g_settings->get("fixed_map_seed").empty())
1902 m_seed = (((u64)(myrand()%0xffff)<<0)
1903 + ((u64)(myrand()%0xffff)<<16)
1904 + ((u64)(myrand()%0xffff)<<32)
1905 + ((u64)(myrand()%0xffff)<<48));
1909 m_seed = g_settings->getU64("fixed_map_seed");
1913 Experimental and debug stuff
1920 Try to load map; if not found, create a new one.
1923 m_savedir = savedir;
1924 m_map_saving_enabled = false;
1928 // If directory exists, check contents and load if possible
1929 if(fs::PathExists(m_savedir))
1931 // If directory is empty, it is safe to save into it.
1932 if(fs::GetDirListing(m_savedir).size() == 0)
1934 infostream<<"ServerMap: Empty save directory is valid."
1936 m_map_saving_enabled = true;
1941 // Load map metadata (seed, chunksize)
1944 catch(FileNotGoodException &e){
1945 infostream<<"WARNING: Could not load map metadata"
1946 //<<" Disabling chunk-based generator."
1951 infostream<<"ServerMap: Successfully loaded map "
1952 <<"metadata from "<<savedir
1953 <<", assuming valid save directory."
1954 <<" seed="<<m_seed<<"."
1957 m_map_saving_enabled = true;
1958 // Map loaded, not creating new one
1962 // If directory doesn't exist, it is safe to save to it
1964 m_map_saving_enabled = true;
1967 catch(std::exception &e)
1969 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
1970 <<", exception: "<<e.what()<<std::endl;
1971 infostream<<"Please remove the map or fix it."<<std::endl;
1972 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1975 infostream<<"Initializing new map."<<std::endl;
1977 // Create zero sector
1978 emergeSector(v2s16(0,0));
1980 // Initially write whole map
1981 save(MOD_STATE_CLEAN);
1984 ServerMap::~ServerMap()
1986 verbosestream<<__FUNCTION_NAME<<std::endl;
1990 if(m_map_saving_enabled)
1992 // Save only changed parts
1993 save(MOD_STATE_WRITE_AT_UNLOAD);
1994 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1998 infostream<<"ServerMap: Map not saved"<<std::endl;
2001 catch(std::exception &e)
2003 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2004 <<", exception: "<<e.what()<<std::endl;
2008 Close database if it was opened
2011 sqlite3_finalize(m_database_read);
2012 if(m_database_write)
2013 sqlite3_finalize(m_database_write);
2015 sqlite3_close(m_database);
2021 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2022 for(; i.atEnd() == false; i++)
2024 MapChunk *chunk = i.getNode()->getValue();
2030 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2032 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2033 if(enable_mapgen_debug_info)
2034 infostream<<"initBlockMake(): "
2035 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2036 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2039 //s16 chunksize = 3;
2040 //v3s16 chunk_offset(-1,-1,-1);
2041 //s16 chunksize = 4;
2042 //v3s16 chunk_offset(-1,-1,-1);
2044 v3s16 chunk_offset(-2,-2,-2);
2045 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2046 v3s16 blockpos_min = blockpos_div * chunksize;
2047 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2048 blockpos_min += chunk_offset;
2049 blockpos_max += chunk_offset;
2051 //v3s16 extra_borders(1,1,1);
2052 v3s16 extra_borders(1,1,1);
2054 // Do nothing if not inside limits (+-1 because of neighbors)
2055 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2056 blockpos_over_limit(blockpos_max + extra_borders))
2062 data->no_op = false;
2063 data->seed = m_seed;
2064 data->blockpos_min = blockpos_min;
2065 data->blockpos_max = blockpos_max;
2066 data->blockpos_requested = blockpos;
2067 data->nodedef = m_gamedef->ndef();
2070 Create the whole area of this and the neighboring blocks
2073 //TimeTaker timer("initBlockMake() create area");
2075 for(s16 x=blockpos_min.X-extra_borders.X;
2076 x<=blockpos_max.X+extra_borders.X; x++)
2077 for(s16 z=blockpos_min.Z-extra_borders.Z;
2078 z<=blockpos_max.Z+extra_borders.Z; z++)
2080 v2s16 sectorpos(x, z);
2081 // Sector metadata is loaded from disk if not already loaded.
2082 ServerMapSector *sector = createSector(sectorpos);
2085 for(s16 y=blockpos_min.Y-extra_borders.Y;
2086 y<=blockpos_max.Y+extra_borders.Y; y++)
2089 //MapBlock *block = createBlock(p);
2090 // 1) get from memory, 2) load from disk
2091 MapBlock *block = emergeBlock(p, false);
2092 // 3) create a blank one
2095 block = createBlock(p);
2098 Block gets sunlight if this is true.
2100 Refer to the map generator heuristics.
2102 bool ug = mapgen::block_is_underground(data->seed, p);
2103 block->setIsUnderground(ug);
2106 // Lighting will not be valid after make_chunk is called
2107 block->setLightingExpired(true);
2108 // Lighting will be calculated
2109 //block->setLightingExpired(false);
2115 Now we have a big empty area.
2117 Make a ManualMapVoxelManipulator that contains this and the
2121 // The area that contains this block and it's neighbors
2122 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2123 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2125 data->vmanip = new ManualMapVoxelManipulator(this);
2126 //data->vmanip->setMap(this);
2130 //TimeTaker timer("initBlockMake() initialEmerge");
2131 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2134 // Data is ready now.
2137 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2138 core::map<v3s16, MapBlock*> &changed_blocks)
2140 v3s16 blockpos_min = data->blockpos_min;
2141 v3s16 blockpos_max = data->blockpos_max;
2142 v3s16 blockpos_requested = data->blockpos_requested;
2143 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2144 <<blockpos_requested.Y<<","
2145 <<blockpos_requested.Z<<")"<<std::endl;*/
2147 v3s16 extra_borders(1,1,1);
2151 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2155 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2157 /*infostream<<"Resulting vmanip:"<<std::endl;
2158 data->vmanip.print(infostream);*/
2160 // Make sure affected blocks are loaded
2161 for(s16 x=blockpos_min.X-extra_borders.X;
2162 x<=blockpos_max.X+extra_borders.X; x++)
2163 for(s16 z=blockpos_min.Z-extra_borders.Z;
2164 z<=blockpos_max.Z+extra_borders.Z; z++)
2165 for(s16 y=blockpos_min.Y-extra_borders.Y;
2166 y<=blockpos_max.Y+extra_borders.Y; y++)
2169 // Load from disk if not already in memory
2170 emergeBlock(p, false);
2174 Blit generated stuff to map
2175 NOTE: blitBackAll adds nearly everything to changed_blocks
2179 //TimeTaker timer("finishBlockMake() blitBackAll");
2180 data->vmanip->blitBackAll(&changed_blocks);
2183 if(enable_mapgen_debug_info)
2184 infostream<<"finishBlockMake: changed_blocks.size()="
2185 <<changed_blocks.size()<<std::endl;
2188 Copy transforming liquid information
2190 while(data->transforming_liquid.size() > 0)
2192 v3s16 p = data->transforming_liquid.pop_front();
2193 m_transforming_liquid.push_back(p);
2197 Do stuff in central blocks
2205 TimeTaker t("finishBlockMake lighting update");
2207 core::map<v3s16, MapBlock*> lighting_update_blocks;
2210 for(s16 x=blockpos_min.X-extra_borders.X;
2211 x<=blockpos_max.X+extra_borders.X; x++)
2212 for(s16 z=blockpos_min.Z-extra_borders.Z;
2213 z<=blockpos_max.Z+extra_borders.Z; z++)
2214 for(s16 y=blockpos_min.Y-extra_borders.Y;
2215 y<=blockpos_max.Y+extra_borders.Y; y++)
2218 MapBlock *block = getBlockNoCreateNoEx(p);
2220 lighting_update_blocks.insert(block->getPos(), block);
2223 updateLighting(lighting_update_blocks, changed_blocks);
2227 Set lighting to non-expired state in all of them.
2228 This is cheating, but it is not fast enough if all of them
2229 would actually be updated.
2231 for(s16 x=blockpos_min.X-extra_borders.X;
2232 x<=blockpos_max.X+extra_borders.X; x++)
2233 for(s16 z=blockpos_min.Z-extra_borders.Z;
2234 z<=blockpos_max.Z+extra_borders.Z; z++)
2235 for(s16 y=blockpos_min.Y-extra_borders.Y;
2236 y<=blockpos_max.Y+extra_borders.Y; y++)
2239 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2243 if(enable_mapgen_debug_info == false)
2244 t.stop(true); // Hide output
2249 Go through changed blocks
2251 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2252 i.atEnd() == false; i++)
2254 MapBlock *block = i.getNode()->getValue();
2257 Update day/night difference cache of the MapBlocks
2259 block->expireDayNightDiff();
2261 Set block as modified
2263 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2264 "finishBlockMake expireDayNightDiff");
2268 Set central blocks as generated
2270 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2271 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2272 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2275 MapBlock *block = getBlockNoCreateNoEx(p);
2277 block->setGenerated(true);
2281 Save changed parts of map
2282 NOTE: Will be saved later.
2284 //save(MOD_STATE_WRITE_AT_UNLOAD);
2286 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2287 <<","<<blockpos_requested.Y<<","
2288 <<blockpos_requested.Z<<")"<<std::endl;*/
2290 if(enable_mapgen_debug_info)
2293 Analyze resulting blocks
2295 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2296 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2297 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2298 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2299 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2300 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2302 v3s16 p = v3s16(x,y,z);
2303 MapBlock *block = getBlockNoCreateNoEx(p);
2305 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2306 infostream<<"Generated "<<spos<<": "
2307 <<analyze_block(block)<<std::endl;
2312 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2318 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2320 DSTACKF("%s: p2d=(%d,%d)",
2325 Check if it exists already in memory
2327 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2332 Try to load it from disk (with blocks)
2334 //if(loadSectorFull(p2d) == true)
2337 Try to load metadata from disk
2340 if(loadSectorMeta(p2d) == true)
2342 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2345 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2346 throw InvalidPositionException("");
2352 Do not create over-limit
2354 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2355 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2356 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2357 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2358 throw InvalidPositionException("createSector(): pos. over limit");
2361 Generate blank sector
2364 sector = new ServerMapSector(this, p2d, m_gamedef);
2366 // Sector position on map in nodes
2367 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2372 m_sectors.insert(p2d, sector);
2378 This is a quick-hand function for calling makeBlock().
2380 MapBlock * ServerMap::generateBlock(
2382 core::map<v3s16, MapBlock*> &modified_blocks
2385 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2387 /*infostream<<"generateBlock(): "
2388 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2391 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2393 TimeTaker timer("generateBlock");
2395 //MapBlock *block = original_dummy;
2397 v2s16 p2d(p.X, p.Z);
2398 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2401 Do not generate over-limit
2403 if(blockpos_over_limit(p))
2405 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2406 throw InvalidPositionException("generateBlock(): pos. over limit");
2410 Create block make data
2412 mapgen::BlockMakeData data;
2413 initBlockMake(&data, p);
2419 TimeTaker t("mapgen::make_block()");
2420 mapgen::make_block(&data);
2422 if(enable_mapgen_debug_info == false)
2423 t.stop(true); // Hide output
2427 Blit data back on map, update lighting, add mobs and whatever this does
2429 finishBlockMake(&data, modified_blocks);
2434 MapBlock *block = getBlockNoCreateNoEx(p);
2442 bool erroneus_content = false;
2443 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2444 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2445 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2448 MapNode n = block->getNode(p);
2449 if(n.getContent() == CONTENT_IGNORE)
2451 infostream<<"CONTENT_IGNORE at "
2452 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2454 erroneus_content = true;
2458 if(erroneus_content)
2467 Generate a completely empty block
2471 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2472 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2474 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2477 n.setContent(CONTENT_AIR);
2478 block->setNode(v3s16(x0,y0,z0), n);
2484 if(enable_mapgen_debug_info == false)
2485 timer.stop(true); // Hide output
2490 MapBlock * ServerMap::createBlock(v3s16 p)
2492 DSTACKF("%s: p=(%d,%d,%d)",
2493 __FUNCTION_NAME, p.X, p.Y, p.Z);
2496 Do not create over-limit
2498 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2499 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2500 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2501 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2502 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2503 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2504 throw InvalidPositionException("createBlock(): pos. over limit");
2506 v2s16 p2d(p.X, p.Z);
2509 This will create or load a sector if not found in memory.
2510 If block exists on disk, it will be loaded.
2512 NOTE: On old save formats, this will be slow, as it generates
2513 lighting on blocks for them.
2515 ServerMapSector *sector;
2517 sector = (ServerMapSector*)createSector(p2d);
2518 assert(sector->getId() == MAPSECTOR_SERVER);
2520 catch(InvalidPositionException &e)
2522 infostream<<"createBlock: createSector() failed"<<std::endl;
2526 NOTE: This should not be done, or at least the exception
2527 should not be passed on as std::exception, because it
2528 won't be catched at all.
2530 /*catch(std::exception &e)
2532 infostream<<"createBlock: createSector() failed: "
2533 <<e.what()<<std::endl;
2538 Try to get a block from the sector
2541 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2544 if(block->isDummy())
2549 block = sector->createBlankBlock(block_y);
2553 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2555 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2557 p.X, p.Y, p.Z, allow_generate);
2560 MapBlock *block = getBlockNoCreateNoEx(p);
2561 if(block && block->isDummy() == false)
2566 MapBlock *block = loadBlock(p);
2573 core::map<v3s16, MapBlock*> modified_blocks;
2574 MapBlock *block = generateBlock(p, modified_blocks);
2578 event.type = MEET_OTHER;
2581 // Copy modified_blocks to event
2582 for(core::map<v3s16, MapBlock*>::Iterator
2583 i = modified_blocks.getIterator();
2584 i.atEnd()==false; i++)
2586 event.modified_blocks.insert(i.getNode()->getKey(), false);
2590 dispatchEvent(&event);
2599 s16 ServerMap::findGroundLevel(v2s16 p2d)
2603 Uh, just do something random...
2605 // Find existing map from top to down
2608 v3s16 p(p2d.X, max, p2d.Y);
2609 for(; p.Y>min; p.Y--)
2611 MapNode n = getNodeNoEx(p);
2612 if(n.getContent() != CONTENT_IGNORE)
2617 // If this node is not air, go to plan b
2618 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2620 // Search existing walkable and return it
2621 for(; p.Y>min; p.Y--)
2623 MapNode n = getNodeNoEx(p);
2624 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2633 Determine from map generator noise functions
2636 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2639 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2640 //return (s16)level;
2643 void ServerMap::createDatabase() {
2646 e = sqlite3_exec(m_database,
2647 "CREATE TABLE IF NOT EXISTS `blocks` ("
2648 "`pos` INT NOT NULL PRIMARY KEY,"
2651 , NULL, NULL, NULL);
2652 if(e == SQLITE_ABORT)
2653 throw FileNotGoodException("Could not create database structure");
2655 infostream<<"ServerMap: Database structure was created";
2658 void ServerMap::verifyDatabase() {
2663 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2664 bool needs_create = false;
2668 Open the database connection
2671 createDirs(m_savedir);
2673 if(!fs::PathExists(dbp))
2674 needs_create = true;
2676 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2677 if(d != SQLITE_OK) {
2678 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2679 throw FileNotGoodException("Cannot open database file");
2685 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2686 if(d != SQLITE_OK) {
2687 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2688 throw FileNotGoodException("Cannot prepare read statement");
2691 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2692 if(d != SQLITE_OK) {
2693 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2694 throw FileNotGoodException("Cannot prepare write statement");
2697 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2698 if(d != SQLITE_OK) {
2699 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2700 throw FileNotGoodException("Cannot prepare read statement");
2703 infostream<<"ServerMap: Database opened"<<std::endl;
2707 bool ServerMap::loadFromFolders() {
2708 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2713 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2714 return (sqlite3_int64)pos.Z*16777216 +
2715 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2718 void ServerMap::createDirs(std::string path)
2720 if(fs::CreateAllDirs(path) == false)
2722 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2723 <<"\""<<path<<"\""<<std::endl;
2724 throw BaseException("ServerMap failed to create directory");
2728 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2734 snprintf(cc, 9, "%.4x%.4x",
2735 (unsigned int)pos.X&0xffff,
2736 (unsigned int)pos.Y&0xffff);
2738 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2740 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2741 (unsigned int)pos.X&0xfff,
2742 (unsigned int)pos.Y&0xfff);
2744 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2750 v2s16 ServerMap::getSectorPos(std::string dirname)
2754 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2755 assert(spos != std::string::npos);
2756 if(dirname.size() - spos == 8)
2759 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2761 else if(dirname.size() - spos == 3)
2764 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2765 // Sign-extend the 12 bit values up to 16 bits...
2766 if(x&0x800) x|=0xF000;
2767 if(y&0x800) y|=0xF000;
2774 v2s16 pos((s16)x, (s16)y);
2778 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2780 v2s16 p2d = getSectorPos(sectordir);
2782 if(blockfile.size() != 4){
2783 throw InvalidFilenameException("Invalid block filename");
2786 int r = sscanf(blockfile.c_str(), "%4x", &y);
2788 throw InvalidFilenameException("Invalid block filename");
2789 return v3s16(p2d.X, y, p2d.Y);
2792 std::string ServerMap::getBlockFilename(v3s16 p)
2795 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2799 void ServerMap::save(ModifiedState save_level)
2801 DSTACK(__FUNCTION_NAME);
2802 if(m_map_saving_enabled == false)
2804 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2808 if(save_level == MOD_STATE_CLEAN)
2809 infostream<<"ServerMap: Saving whole map, this can take time."
2812 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2817 // Profile modified reasons
2818 Profiler modprofiler;
2820 u32 sector_meta_count = 0;
2821 u32 block_count = 0;
2822 u32 block_count_all = 0; // Number of blocks in memory
2824 // Don't do anything with sqlite unless something is really saved
2825 bool save_started = false;
2827 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2828 for(; i.atEnd() == false; i++)
2830 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2831 assert(sector->getId() == MAPSECTOR_SERVER);
2833 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2835 saveSectorMeta(sector);
2836 sector_meta_count++;
2838 core::list<MapBlock*> blocks;
2839 sector->getBlocks(blocks);
2840 core::list<MapBlock*>::Iterator j;
2842 for(j=blocks.begin(); j!=blocks.end(); j++)
2844 MapBlock *block = *j;
2848 if(block->getModified() >= save_level)
2853 save_started = true;
2856 modprofiler.add(block->getModifiedReason(), 1);
2861 /*infostream<<"ServerMap: Written block ("
2862 <<block->getPos().X<<","
2863 <<block->getPos().Y<<","
2864 <<block->getPos().Z<<")"
2873 Only print if something happened or saved whole map
2875 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2876 || block_count != 0)
2878 infostream<<"ServerMap: Written: "
2879 <<sector_meta_count<<" sector metadata files, "
2880 <<block_count<<" block files"
2881 <<", "<<block_count_all<<" blocks in memory."
2883 PrintInfo(infostream); // ServerMap/ClientMap:
2884 infostream<<"Blocks modified by: "<<std::endl;
2885 modprofiler.print(infostream);
2889 static s32 unsignedToSigned(s32 i, s32 max_positive)
2891 if(i < max_positive)
2894 return i - 2*max_positive;
2897 // modulo of a negative number does not work consistently in C
2898 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2902 return mod - ((-i) % mod);
2905 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2907 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2909 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2911 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2912 return v3s16(x,y,z);
2915 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2917 if(loadFromFolders()){
2918 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2919 <<"all blocks that are stored in flat files"<<std::endl;
2925 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2927 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2928 v3s16 p = getIntegerAsBlock(block_i);
2929 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2935 void ServerMap::saveMapMeta()
2937 DSTACK(__FUNCTION_NAME);
2939 /*infostream<<"ServerMap::saveMapMeta(): "
2943 createDirs(m_savedir);
2945 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2946 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2947 if(os.good() == false)
2949 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2950 <<"could not open"<<fullpath<<std::endl;
2951 throw FileNotGoodException("Cannot open chunk metadata");
2955 params.setU64("seed", m_seed);
2957 params.writeLines(os);
2959 os<<"[end_of_params]\n";
2961 m_map_metadata_changed = false;
2964 void ServerMap::loadMapMeta()
2966 DSTACK(__FUNCTION_NAME);
2968 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2971 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2972 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2973 if(is.good() == false)
2975 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2976 <<"could not open"<<fullpath<<std::endl;
2977 throw FileNotGoodException("Cannot open map metadata");
2985 throw SerializationError
2986 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2988 std::getline(is, line);
2989 std::string trimmedline = trim(line);
2990 if(trimmedline == "[end_of_params]")
2992 params.parseConfigLine(line);
2995 m_seed = params.getU64("seed");
2997 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3000 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3002 DSTACK(__FUNCTION_NAME);
3003 // Format used for writing
3004 u8 version = SER_FMT_VER_HIGHEST;
3006 v2s16 pos = sector->getPos();
3007 std::string dir = getSectorDir(pos);
3010 std::string fullpath = dir + DIR_DELIM + "meta";
3011 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3012 if(o.good() == false)
3013 throw FileNotGoodException("Cannot open sector metafile");
3015 sector->serialize(o, version);
3017 sector->differs_from_disk = false;
3020 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3022 DSTACK(__FUNCTION_NAME);
3024 v2s16 p2d = getSectorPos(sectordir);
3026 ServerMapSector *sector = NULL;
3028 std::string fullpath = sectordir + DIR_DELIM + "meta";
3029 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3030 if(is.good() == false)
3032 // If the directory exists anyway, it probably is in some old
3033 // format. Just go ahead and create the sector.
3034 if(fs::PathExists(sectordir))
3036 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3037 <<fullpath<<" doesn't exist but directory does."
3038 <<" Continuing with a sector with no metadata."
3040 sector = new ServerMapSector(this, p2d, m_gamedef);
3041 m_sectors.insert(p2d, sector);
3045 throw FileNotGoodException("Cannot open sector metafile");
3050 sector = ServerMapSector::deSerialize
3051 (is, this, p2d, m_sectors, m_gamedef);
3053 saveSectorMeta(sector);
3056 sector->differs_from_disk = false;
3061 bool ServerMap::loadSectorMeta(v2s16 p2d)
3063 DSTACK(__FUNCTION_NAME);
3065 MapSector *sector = NULL;
3067 // The directory layout we're going to load from.
3068 // 1 - original sectors/xxxxzzzz/
3069 // 2 - new sectors2/xxx/zzz/
3070 // If we load from anything but the latest structure, we will
3071 // immediately save to the new one, and remove the old.
3073 std::string sectordir1 = getSectorDir(p2d, 1);
3074 std::string sectordir;
3075 if(fs::PathExists(sectordir1))
3077 sectordir = sectordir1;
3082 sectordir = getSectorDir(p2d, 2);
3086 sector = loadSectorMeta(sectordir, loadlayout != 2);
3088 catch(InvalidFilenameException &e)
3092 catch(FileNotGoodException &e)
3096 catch(std::exception &e)
3105 bool ServerMap::loadSectorFull(v2s16 p2d)
3107 DSTACK(__FUNCTION_NAME);
3109 MapSector *sector = NULL;
3111 // The directory layout we're going to load from.
3112 // 1 - original sectors/xxxxzzzz/
3113 // 2 - new sectors2/xxx/zzz/
3114 // If we load from anything but the latest structure, we will
3115 // immediately save to the new one, and remove the old.
3117 std::string sectordir1 = getSectorDir(p2d, 1);
3118 std::string sectordir;
3119 if(fs::PathExists(sectordir1))
3121 sectordir = sectordir1;
3126 sectordir = getSectorDir(p2d, 2);
3130 sector = loadSectorMeta(sectordir, loadlayout != 2);
3132 catch(InvalidFilenameException &e)
3136 catch(FileNotGoodException &e)
3140 catch(std::exception &e)
3148 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3150 std::vector<fs::DirListNode>::iterator i2;
3151 for(i2=list2.begin(); i2!=list2.end(); i2++)
3157 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3159 catch(InvalidFilenameException &e)
3161 // This catches unknown crap in directory
3167 infostream<<"Sector converted to new layout - deleting "<<
3168 sectordir1<<std::endl;
3169 fs::RecursiveDelete(sectordir1);
3176 void ServerMap::beginSave() {
3178 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3179 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3182 void ServerMap::endSave() {
3184 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3185 infostream<<"WARNING: endSave() failed, map might not have saved.";
3188 void ServerMap::saveBlock(MapBlock *block)
3190 DSTACK(__FUNCTION_NAME);
3192 Dummy blocks are not written
3194 if(block->isDummy())
3196 /*v3s16 p = block->getPos();
3197 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3198 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3202 // Format used for writing
3203 u8 version = SER_FMT_VER_HIGHEST;
3205 v3s16 p3d = block->getPos();
3209 v2s16 p2d(p3d.X, p3d.Z);
3210 std::string sectordir = getSectorDir(p2d);
3212 createDirs(sectordir);
3214 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3215 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3216 if(o.good() == false)
3217 throw FileNotGoodException("Cannot open block data");
3220 [0] u8 serialization version
3226 std::ostringstream o(std::ios_base::binary);
3228 o.write((char*)&version, 1);
3231 block->serialize(o, version, true);
3233 // Write block to database
3235 std::string tmp = o.str();
3236 const char *bytes = tmp.c_str();
3238 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3239 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3240 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3241 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3242 int written = sqlite3_step(m_database_write);
3243 if(written != SQLITE_DONE)
3244 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3245 <<sqlite3_errmsg(m_database)<<std::endl;
3246 // Make ready for later reuse
3247 sqlite3_reset(m_database_write);
3249 // We just wrote it to the disk so clear modified flag
3250 block->resetModified();
3253 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3255 DSTACK(__FUNCTION_NAME);
3257 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3260 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3261 if(is.good() == false)
3262 throw FileNotGoodException("Cannot open block file");
3264 v3s16 p3d = getBlockPos(sectordir, blockfile);
3265 v2s16 p2d(p3d.X, p3d.Z);
3267 assert(sector->getPos() == p2d);
3269 u8 version = SER_FMT_VER_INVALID;
3270 is.read((char*)&version, 1);
3273 throw SerializationError("ServerMap::loadBlock(): Failed"
3274 " to read MapBlock version");
3276 /*u32 block_size = MapBlock::serializedLength(version);
3277 SharedBuffer<u8> data(block_size);
3278 is.read((char*)*data, block_size);*/
3280 // This will always return a sector because we're the server
3281 //MapSector *sector = emergeSector(p2d);
3283 MapBlock *block = NULL;
3284 bool created_new = false;
3285 block = sector->getBlockNoCreateNoEx(p3d.Y);
3288 block = sector->createBlankBlockNoInsert(p3d.Y);
3293 block->deSerialize(is, version, true);
3295 // If it's a new block, insert it to the map
3297 sector->insertBlock(block);
3300 Save blocks loaded in old format in new format
3303 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3307 // Should be in database now, so delete the old file
3308 fs::RecursiveDelete(fullpath);
3311 // We just loaded it from the disk, so it's up-to-date.
3312 block->resetModified();
3315 catch(SerializationError &e)
3317 infostream<<"WARNING: Invalid block data on disk "
3318 <<"fullpath="<<fullpath
3319 <<" (SerializationError). "
3320 <<"what()="<<e.what()
3322 //" Ignoring. A new one will be generated.
3325 // TODO: Backup file; name is in fullpath.
3329 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3331 DSTACK(__FUNCTION_NAME);
3334 std::istringstream is(*blob, std::ios_base::binary);
3336 u8 version = SER_FMT_VER_INVALID;
3337 is.read((char*)&version, 1);
3340 throw SerializationError("ServerMap::loadBlock(): Failed"
3341 " to read MapBlock version");
3343 /*u32 block_size = MapBlock::serializedLength(version);
3344 SharedBuffer<u8> data(block_size);
3345 is.read((char*)*data, block_size);*/
3347 // This will always return a sector because we're the server
3348 //MapSector *sector = emergeSector(p2d);
3350 MapBlock *block = NULL;
3351 bool created_new = false;
3352 block = sector->getBlockNoCreateNoEx(p3d.Y);
3355 block = sector->createBlankBlockNoInsert(p3d.Y);
3360 block->deSerialize(is, version, true);
3362 // If it's a new block, insert it to the map
3364 sector->insertBlock(block);
3367 Save blocks loaded in old format in new format
3370 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3371 // Only save if asked to; no need to update version
3375 // We just loaded it from, so it's up-to-date.
3376 block->resetModified();
3379 catch(SerializationError &e)
3381 errorstream<<"Invalid block data in database"
3382 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3383 <<" (SerializationError): "<<e.what()<<std::endl;
3385 // TODO: Block should be marked as invalid in memory so that it is
3386 // not touched but the game can run
3388 if(g_settings->getBool("ignore_world_load_errors")){
3389 errorstream<<"Ignoring block load error. Duck and cover! "
3390 <<"(ignore_world_load_errors)"<<std::endl;
3392 throw SerializationError("Invalid block data in database");
3398 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3400 DSTACK(__FUNCTION_NAME);
3402 v2s16 p2d(blockpos.X, blockpos.Z);
3404 if(!loadFromFolders()) {
3407 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3408 infostream<<"WARNING: Could not bind block position for load: "
3409 <<sqlite3_errmsg(m_database)<<std::endl;
3410 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3412 Make sure sector is loaded
3414 MapSector *sector = createSector(p2d);
3419 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3420 size_t len = sqlite3_column_bytes(m_database_read, 0);
3422 std::string datastr(data, len);
3424 loadBlock(&datastr, blockpos, sector, false);
3426 sqlite3_step(m_database_read);
3427 // We should never get more than 1 row, so ok to reset
3428 sqlite3_reset(m_database_read);
3430 return getBlockNoCreateNoEx(blockpos);
3432 sqlite3_reset(m_database_read);
3434 // Not found in database, try the files
3437 // The directory layout we're going to load from.
3438 // 1 - original sectors/xxxxzzzz/
3439 // 2 - new sectors2/xxx/zzz/
3440 // If we load from anything but the latest structure, we will
3441 // immediately save to the new one, and remove the old.
3443 std::string sectordir1 = getSectorDir(p2d, 1);
3444 std::string sectordir;
3445 if(fs::PathExists(sectordir1))
3447 sectordir = sectordir1;
3452 sectordir = getSectorDir(p2d, 2);
3456 Make sure sector is loaded
3458 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3462 sector = loadSectorMeta(sectordir, loadlayout != 2);
3464 catch(InvalidFilenameException &e)
3468 catch(FileNotGoodException &e)
3472 catch(std::exception &e)
3479 Make sure file exists
3482 std::string blockfilename = getBlockFilename(blockpos);
3483 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3487 Load block and save it to the database
3489 loadBlock(sectordir, blockfilename, sector, true);
3490 return getBlockNoCreateNoEx(blockpos);
3493 void ServerMap::PrintInfo(std::ostream &out)
3502 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3507 MapVoxelManipulator::~MapVoxelManipulator()
3509 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3513 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3515 TimeTaker timer1("emerge", &emerge_time);
3517 // Units of these are MapBlocks
3518 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3519 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3521 VoxelArea block_area_nodes
3522 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3524 addArea(block_area_nodes);
3526 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3527 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3528 for(s32 x=p_min.X; x<=p_max.X; x++)
3531 core::map<v3s16, bool>::Node *n;
3532 n = m_loaded_blocks.find(p);
3536 bool block_data_inexistent = false;
3539 TimeTaker timer1("emerge load", &emerge_load_time);
3541 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3542 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3544 a.print(infostream);
3545 infostream<<std::endl;*/
3547 MapBlock *block = m_map->getBlockNoCreate(p);
3548 if(block->isDummy())
3549 block_data_inexistent = true;
3551 block->copyTo(*this);
3553 catch(InvalidPositionException &e)
3555 block_data_inexistent = true;
3558 if(block_data_inexistent)
3560 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3561 // Fill with VOXELFLAG_INEXISTENT
3562 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3563 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3565 s32 i = m_area.index(a.MinEdge.X,y,z);
3566 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3570 m_loaded_blocks.insert(p, !block_data_inexistent);
3573 //infostream<<"emerge done"<<std::endl;
3577 SUGG: Add an option to only update eg. water and air nodes.
3578 This will make it interfere less with important stuff if
3581 void MapVoxelManipulator::blitBack
3582 (core::map<v3s16, MapBlock*> & modified_blocks)
3584 if(m_area.getExtent() == v3s16(0,0,0))
3587 //TimeTaker timer1("blitBack");
3589 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3590 <<m_loaded_blocks.size()<<std::endl;*/
3593 Initialize block cache
3595 v3s16 blockpos_last;
3596 MapBlock *block = NULL;
3597 bool block_checked_in_modified = false;
3599 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3600 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3601 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3605 u8 f = m_flags[m_area.index(p)];
3606 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3609 MapNode &n = m_data[m_area.index(p)];
3611 v3s16 blockpos = getNodeBlockPos(p);
3616 if(block == NULL || blockpos != blockpos_last){
3617 block = m_map->getBlockNoCreate(blockpos);
3618 blockpos_last = blockpos;
3619 block_checked_in_modified = false;
3622 // Calculate relative position in block
3623 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3625 // Don't continue if nothing has changed here
3626 if(block->getNode(relpos) == n)
3629 //m_map->setNode(m_area.MinEdge + p, n);
3630 block->setNode(relpos, n);
3633 Make sure block is in modified_blocks
3635 if(block_checked_in_modified == false)
3637 modified_blocks[blockpos] = block;
3638 block_checked_in_modified = true;
3641 catch(InvalidPositionException &e)
3647 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3648 MapVoxelManipulator(map),
3649 m_create_area(false)
3653 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3657 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3659 // Just create the area so that it can be pointed to
3660 VoxelManipulator::emerge(a, caller_id);
3663 void ManualMapVoxelManipulator::initialEmerge(
3664 v3s16 blockpos_min, v3s16 blockpos_max)
3666 TimeTaker timer1("initialEmerge", &emerge_time);
3668 // Units of these are MapBlocks
3669 v3s16 p_min = blockpos_min;
3670 v3s16 p_max = blockpos_max;
3672 VoxelArea block_area_nodes
3673 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3675 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3678 infostream<<"initialEmerge: area: ";
3679 block_area_nodes.print(infostream);
3680 infostream<<" ("<<size_MB<<"MB)";
3681 infostream<<std::endl;
3684 addArea(block_area_nodes);
3686 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3687 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3688 for(s32 x=p_min.X; x<=p_max.X; x++)
3691 core::map<v3s16, bool>::Node *n;
3692 n = m_loaded_blocks.find(p);
3696 bool block_data_inexistent = false;
3699 TimeTaker timer1("emerge load", &emerge_load_time);
3701 MapBlock *block = m_map->getBlockNoCreate(p);
3702 if(block->isDummy())
3703 block_data_inexistent = true;
3705 block->copyTo(*this);
3707 catch(InvalidPositionException &e)
3709 block_data_inexistent = true;
3712 if(block_data_inexistent)
3715 Mark area inexistent
3717 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3718 // Fill with VOXELFLAG_INEXISTENT
3719 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3720 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3722 s32 i = m_area.index(a.MinEdge.X,y,z);
3723 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3727 m_loaded_blocks.insert(p, !block_data_inexistent);
3731 void ManualMapVoxelManipulator::blitBackAll(
3732 core::map<v3s16, MapBlock*> * modified_blocks)
3734 if(m_area.getExtent() == v3s16(0,0,0))
3738 Copy data of all blocks
3740 for(core::map<v3s16, bool>::Iterator
3741 i = m_loaded_blocks.getIterator();
3742 i.atEnd() == false; i++)
3744 v3s16 p = i.getNode()->getKey();
3745 bool existed = i.getNode()->getValue();
3746 if(existed == false)
3748 // The Great Bug was found using this
3749 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3750 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3754 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3757 infostream<<"WARNING: "<<__FUNCTION_NAME
3758 <<": got NULL block "
3759 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3764 block->copyFrom(*this);
3767 modified_blocks->insert(p, block);