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
1019 If node is under sunlight and doesn't let sunlight through,
1020 take all sunlighted nodes under it and clear light from them
1021 and from where the light has been spread.
1022 TODO: This could be optimized by mass-unlighting instead
1025 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1029 //m_dout<<DTIME<<"y="<<y<<std::endl;
1030 v3s16 n2pos(p.X, y, p.Z);
1034 n2 = getNode(n2pos);
1036 catch(InvalidPositionException &e)
1041 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1043 unLightNeighbors(LIGHTBANK_DAY,
1044 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1045 light_sources, modified_blocks);
1046 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1054 for(s32 i=0; i<2; i++)
1056 enum LightBank bank = banks[i];
1059 Spread light from all nodes that might be capable of doing so
1061 spreadLight(bank, light_sources, modified_blocks);
1065 Update information about whether day and night light differ
1067 for(core::map<v3s16, MapBlock*>::Iterator
1068 i = modified_blocks.getIterator();
1069 i.atEnd() == false; i++)
1071 MapBlock *block = i.getNode()->getValue();
1072 block->expireDayNightDiff();
1076 Add neighboring liquid nodes and the node itself if it is
1077 liquid (=water node was added) to transform queue.
1080 v3s16(0,0,0), // self
1081 v3s16(0,0,1), // back
1082 v3s16(0,1,0), // top
1083 v3s16(1,0,0), // right
1084 v3s16(0,0,-1), // front
1085 v3s16(0,-1,0), // bottom
1086 v3s16(-1,0,0), // left
1088 for(u16 i=0; i<7; i++)
1093 v3s16 p2 = p + dirs[i];
1095 MapNode n2 = getNode(p2);
1096 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1098 m_transforming_liquid.push_back(p2);
1101 }catch(InvalidPositionException &e)
1109 void Map::removeNodeAndUpdate(v3s16 p,
1110 core::map<v3s16, MapBlock*> &modified_blocks)
1112 INodeDefManager *nodemgr = m_gamedef->ndef();
1114 /*PrintInfo(m_dout);
1115 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1116 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1118 bool node_under_sunlight = true;
1120 v3s16 toppos = p + v3s16(0,1,0);
1122 // Node will be replaced with this
1123 content_t replace_material = CONTENT_AIR;
1126 If there is a node at top and it doesn't have sunlight,
1127 there will be no sunlight going down.
1130 MapNode topnode = getNode(toppos);
1132 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1133 node_under_sunlight = false;
1135 catch(InvalidPositionException &e)
1139 core::map<v3s16, bool> light_sources;
1141 enum LightBank banks[] =
1146 for(s32 i=0; i<2; i++)
1148 enum LightBank bank = banks[i];
1151 Unlight neighbors (in case the node is a light source)
1153 unLightNeighbors(bank, p,
1154 getNode(p).getLight(bank, nodemgr),
1155 light_sources, modified_blocks);
1159 Remove node metadata
1162 removeNodeMetadata(p);
1166 This also clears the lighting.
1170 n.setContent(replace_material);
1173 for(s32 i=0; i<2; i++)
1175 enum LightBank bank = banks[i];
1178 Recalculate lighting
1180 spreadLight(bank, light_sources, modified_blocks);
1183 // Add the block of the removed node to modified_blocks
1184 v3s16 blockpos = getNodeBlockPos(p);
1185 MapBlock * block = getBlockNoCreate(blockpos);
1186 assert(block != NULL);
1187 modified_blocks.insert(blockpos, block);
1190 If the removed node was under sunlight, propagate the
1191 sunlight down from it and then light all neighbors
1192 of the propagated blocks.
1194 if(node_under_sunlight)
1196 s16 ybottom = propagateSunlight(p, modified_blocks);
1197 /*m_dout<<DTIME<<"Node was under sunlight. "
1198 "Propagating sunlight";
1199 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1201 for(; y >= ybottom; y--)
1203 v3s16 p2(p.X, y, p.Z);
1204 /*m_dout<<DTIME<<"lighting neighbors of node ("
1205 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1207 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1212 // Set the lighting of this node to 0
1213 // TODO: Is this needed? Lighting is cleared up there already.
1215 MapNode n = getNode(p);
1216 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1219 catch(InvalidPositionException &e)
1225 for(s32 i=0; i<2; i++)
1227 enum LightBank bank = banks[i];
1229 // Get the brightest neighbour node and propagate light from it
1230 v3s16 n2p = getBrightestNeighbour(bank, p);
1232 MapNode n2 = getNode(n2p);
1233 lightNeighbors(bank, n2p, modified_blocks);
1235 catch(InvalidPositionException &e)
1241 Update information about whether day and night light differ
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd() == false; i++)
1247 MapBlock *block = i.getNode()->getValue();
1248 block->expireDayNightDiff();
1252 Add neighboring liquid nodes and this node to transform queue.
1253 (it's vital for the node itself to get updated last.)
1256 v3s16(0,0,1), // back
1257 v3s16(0,1,0), // top
1258 v3s16(1,0,0), // right
1259 v3s16(0,0,-1), // front
1260 v3s16(0,-1,0), // bottom
1261 v3s16(-1,0,0), // left
1262 v3s16(0,0,0), // self
1264 for(u16 i=0; i<7; i++)
1269 v3s16 p2 = p + dirs[i];
1271 MapNode n2 = getNode(p2);
1272 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1274 m_transforming_liquid.push_back(p2);
1277 }catch(InvalidPositionException &e)
1283 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1286 event.type = MEET_ADDNODE;
1290 bool succeeded = true;
1292 core::map<v3s16, MapBlock*> modified_blocks;
1293 addNodeAndUpdate(p, n, modified_blocks);
1295 // Copy modified_blocks to event
1296 for(core::map<v3s16, MapBlock*>::Iterator
1297 i = modified_blocks.getIterator();
1298 i.atEnd()==false; i++)
1300 event.modified_blocks.insert(i.getNode()->getKey(), false);
1303 catch(InvalidPositionException &e){
1307 dispatchEvent(&event);
1312 bool Map::removeNodeWithEvent(v3s16 p)
1315 event.type = MEET_REMOVENODE;
1318 bool succeeded = true;
1320 core::map<v3s16, MapBlock*> modified_blocks;
1321 removeNodeAndUpdate(p, modified_blocks);
1323 // Copy modified_blocks to event
1324 for(core::map<v3s16, MapBlock*>::Iterator
1325 i = modified_blocks.getIterator();
1326 i.atEnd()==false; i++)
1328 event.modified_blocks.insert(i.getNode()->getKey(), false);
1331 catch(InvalidPositionException &e){
1335 dispatchEvent(&event);
1340 bool Map::getDayNightDiff(v3s16 blockpos)
1343 v3s16 p = blockpos + v3s16(0,0,0);
1344 MapBlock *b = getBlockNoCreate(p);
1345 if(b->getDayNightDiff())
1348 catch(InvalidPositionException &e){}
1351 v3s16 p = blockpos + v3s16(-1,0,0);
1352 MapBlock *b = getBlockNoCreate(p);
1353 if(b->getDayNightDiff())
1356 catch(InvalidPositionException &e){}
1358 v3s16 p = blockpos + v3s16(0,-1,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->getDayNightDiff())
1363 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(0,0,-1);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->getDayNightDiff())
1370 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(1,0,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->getDayNightDiff())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,1,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->getDayNightDiff())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,0,1);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->getDayNightDiff())
1392 catch(InvalidPositionException &e){}
1398 Updates usage timers
1400 void Map::timerUpdate(float dtime, float unload_timeout,
1401 core::list<v3s16> *unloaded_blocks)
1403 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1405 // Profile modified reasons
1406 Profiler modprofiler;
1408 core::list<v2s16> sector_deletion_queue;
1409 u32 deleted_blocks_count = 0;
1410 u32 saved_blocks_count = 0;
1411 u32 block_count_all = 0;
1413 core::map<v2s16, MapSector*>::Iterator si;
1416 si = m_sectors.getIterator();
1417 for(; si.atEnd() == false; si++)
1419 MapSector *sector = si.getNode()->getValue();
1421 bool all_blocks_deleted = true;
1423 core::list<MapBlock*> blocks;
1424 sector->getBlocks(blocks);
1426 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1427 i != blocks.end(); i++)
1429 MapBlock *block = (*i);
1431 block->incrementUsageTimer(dtime);
1433 if(block->getUsageTimer() > unload_timeout)
1435 v3s16 p = block->getPos();
1438 if(block->getModified() != MOD_STATE_CLEAN
1439 && save_before_unloading)
1441 modprofiler.add(block->getModifiedReason(), 1);
1443 saved_blocks_count++;
1446 // Delete from memory
1447 sector->deleteBlock(block);
1450 unloaded_blocks->push_back(p);
1452 deleted_blocks_count++;
1456 all_blocks_deleted = false;
1461 if(all_blocks_deleted)
1463 sector_deletion_queue.push_back(si.getNode()->getKey());
1468 // Finally delete the empty sectors
1469 deleteSectors(sector_deletion_queue);
1471 if(deleted_blocks_count != 0)
1473 PrintInfo(infostream); // ServerMap/ClientMap:
1474 infostream<<"Unloaded "<<deleted_blocks_count
1475 <<" blocks from memory";
1476 if(save_before_unloading)
1477 infostream<<", of which "<<saved_blocks_count<<" were written";
1478 infostream<<", "<<block_count_all<<" blocks in memory";
1479 infostream<<"."<<std::endl;
1480 if(saved_blocks_count != 0){
1481 PrintInfo(infostream); // ServerMap/ClientMap:
1482 infostream<<"Blocks modified by: "<<std::endl;
1483 modprofiler.print(infostream);
1488 void Map::deleteSectors(core::list<v2s16> &list)
1490 core::list<v2s16>::Iterator j;
1491 for(j=list.begin(); j!=list.end(); j++)
1493 MapSector *sector = m_sectors[*j];
1494 // If sector is in sector cache, remove it from there
1495 if(m_sector_cache == sector)
1496 m_sector_cache = NULL;
1497 // Remove from map and delete
1498 m_sectors.remove(*j);
1504 void Map::unloadUnusedData(float timeout,
1505 core::list<v3s16> *deleted_blocks)
1507 core::list<v2s16> sector_deletion_queue;
1508 u32 deleted_blocks_count = 0;
1509 u32 saved_blocks_count = 0;
1511 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1512 for(; si.atEnd() == false; si++)
1514 MapSector *sector = si.getNode()->getValue();
1516 bool all_blocks_deleted = true;
1518 core::list<MapBlock*> blocks;
1519 sector->getBlocks(blocks);
1520 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1521 i != blocks.end(); i++)
1523 MapBlock *block = (*i);
1525 if(block->getUsageTimer() > timeout)
1528 if(block->getModified() != MOD_STATE_CLEAN)
1531 saved_blocks_count++;
1533 // Delete from memory
1534 sector->deleteBlock(block);
1535 deleted_blocks_count++;
1539 all_blocks_deleted = false;
1543 if(all_blocks_deleted)
1545 sector_deletion_queue.push_back(si.getNode()->getKey());
1549 deleteSectors(sector_deletion_queue);
1551 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1552 <<", of which "<<saved_blocks_count<<" were wr."
1555 //return sector_deletion_queue.getSize();
1556 //return deleted_blocks_count;
1560 void Map::PrintInfo(std::ostream &out)
1565 #define WATER_DROP_BOOST 4
1569 NEIGHBOR_SAME_LEVEL,
1572 struct NodeNeighbor {
1578 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1580 INodeDefManager *nodemgr = m_gamedef->ndef();
1582 DSTACK(__FUNCTION_NAME);
1583 //TimeTaker timer("transformLiquids()");
1586 u32 initial_size = m_transforming_liquid.size();
1588 /*if(initial_size != 0)
1589 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1591 // list of nodes that due to viscosity have not reached their max level height
1592 UniqueQueue<v3s16> must_reflow;
1594 // List of MapBlocks that will require a lighting update (due to lava)
1595 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1597 while(m_transforming_liquid.size() != 0)
1599 // This should be done here so that it is done when continue is used
1600 if(loopcount >= initial_size * 3)
1605 Get a queued transforming liquid node
1607 v3s16 p0 = m_transforming_liquid.pop_front();
1609 MapNode n0 = getNodeNoEx(p0);
1612 Collect information about current node
1614 s8 liquid_level = -1;
1615 u8 liquid_kind = CONTENT_IGNORE;
1616 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1617 switch (liquid_type) {
1619 liquid_level = LIQUID_LEVEL_SOURCE;
1620 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1622 case LIQUID_FLOWING:
1623 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1624 liquid_kind = n0.getContent();
1627 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1628 // continue with the next node.
1629 if (n0.getContent() != CONTENT_AIR)
1631 liquid_kind = CONTENT_AIR;
1636 Collect information about the environment
1638 const v3s16 *dirs = g_6dirs;
1639 NodeNeighbor sources[6]; // surrounding sources
1640 int num_sources = 0;
1641 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1643 NodeNeighbor airs[6]; // surrounding air
1645 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1646 int num_neutrals = 0;
1647 bool flowing_down = false;
1648 for (u16 i = 0; i < 6; i++) {
1649 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1652 nt = NEIGHBOR_UPPER;
1655 nt = NEIGHBOR_LOWER;
1658 v3s16 npos = p0 + dirs[i];
1659 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1660 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1662 if (nb.n.getContent() == CONTENT_AIR) {
1663 airs[num_airs++] = nb;
1664 // if the current node is a water source the neighbor
1665 // should be enqueded for transformation regardless of whether the
1666 // current node changes or not.
1667 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1668 m_transforming_liquid.push_back(npos);
1669 // if the current node happens to be a flowing node, it will start to flow down here.
1670 if (nb.t == NEIGHBOR_LOWER) {
1671 flowing_down = true;
1674 neutrals[num_neutrals++] = nb;
1678 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1679 if (liquid_kind == CONTENT_AIR)
1680 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1681 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1682 neutrals[num_neutrals++] = nb;
1684 // Do not count bottom source, it will screw things up
1686 sources[num_sources++] = nb;
1689 case LIQUID_FLOWING:
1690 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1691 if (liquid_kind == CONTENT_AIR)
1692 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1693 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1694 neutrals[num_neutrals++] = nb;
1696 flows[num_flows++] = nb;
1697 if (nb.t == NEIGHBOR_LOWER)
1698 flowing_down = true;
1705 decide on the type (and possibly level) of the current node
1707 content_t new_node_content;
1708 s8 new_node_level = -1;
1709 s8 max_node_level = -1;
1710 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1711 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1712 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1713 // it's perfectly safe to use liquid_kind here to determine the new node content.
1714 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1715 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1716 // liquid_kind is set properly, see above
1717 new_node_content = liquid_kind;
1718 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1720 // no surrounding sources, so get the maximum level that can flow into this node
1721 for (u16 i = 0; i < num_flows; i++) {
1722 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1723 switch (flows[i].t) {
1724 case NEIGHBOR_UPPER:
1725 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1726 max_node_level = LIQUID_LEVEL_MAX;
1727 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1728 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1729 } else if (nb_liquid_level > max_node_level)
1730 max_node_level = nb_liquid_level;
1732 case NEIGHBOR_LOWER:
1734 case NEIGHBOR_SAME_LEVEL:
1735 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1736 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1737 max_node_level = nb_liquid_level - 1;
1743 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1744 if (viscosity > 1 && max_node_level != liquid_level) {
1745 // amount to gain, limited by viscosity
1746 // must be at least 1 in absolute value
1747 s8 level_inc = max_node_level - liquid_level;
1748 if (level_inc < -viscosity || level_inc > viscosity)
1749 new_node_level = liquid_level + level_inc/viscosity;
1750 else if (level_inc < 0)
1751 new_node_level = liquid_level - 1;
1752 else if (level_inc > 0)
1753 new_node_level = liquid_level + 1;
1754 if (new_node_level != max_node_level)
1755 must_reflow.push_back(p0);
1757 new_node_level = max_node_level;
1759 if (new_node_level >= 0)
1760 new_node_content = liquid_kind;
1762 new_node_content = CONTENT_AIR;
1767 check if anything has changed. if not, just continue with the next node.
1769 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1770 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1771 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1777 update the current node
1779 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1780 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1781 // set level to last 3 bits, flowing down bit to 4th bit
1782 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1784 // set the liquid level and flow bit to 0
1785 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1787 n0.setContent(new_node_content);
1789 v3s16 blockpos = getNodeBlockPos(p0);
1790 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1792 modified_blocks.insert(blockpos, block);
1793 // If node emits light, MapBlock requires lighting update
1794 if(nodemgr->get(n0).light_source != 0)
1795 lighting_modified_blocks[block->getPos()] = block;
1799 enqueue neighbors for update if neccessary
1801 switch (nodemgr->get(n0.getContent()).liquid_type) {
1803 case LIQUID_FLOWING:
1804 // make sure source flows into all neighboring nodes
1805 for (u16 i = 0; i < num_flows; i++)
1806 if (flows[i].t != NEIGHBOR_UPPER)
1807 m_transforming_liquid.push_back(flows[i].p);
1808 for (u16 i = 0; i < num_airs; i++)
1809 if (airs[i].t != NEIGHBOR_UPPER)
1810 m_transforming_liquid.push_back(airs[i].p);
1813 // this flow has turned to air; neighboring flows might need to do the same
1814 for (u16 i = 0; i < num_flows; i++)
1815 m_transforming_liquid.push_back(flows[i].p);
1819 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1820 while (must_reflow.size() > 0)
1821 m_transforming_liquid.push_back(must_reflow.pop_front());
1822 updateLighting(lighting_modified_blocks, modified_blocks);
1825 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1827 v3s16 blockpos = getNodeBlockPos(p);
1828 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1829 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1831 infostream<<"Map::getNodeMetadata(): Need to emerge "
1832 <<PP(blockpos)<<std::endl;
1833 block = emergeBlock(blockpos, false);
1837 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1841 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1845 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1847 v3s16 blockpos = getNodeBlockPos(p);
1848 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1849 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1851 infostream<<"Map::setNodeMetadata(): Need to emerge "
1852 <<PP(blockpos)<<std::endl;
1853 block = emergeBlock(blockpos, false);
1857 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1861 block->m_node_metadata.set(p_rel, meta);
1864 void Map::removeNodeMetadata(v3s16 p)
1866 v3s16 blockpos = getNodeBlockPos(p);
1867 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1868 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1871 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1875 block->m_node_metadata.remove(p_rel);
1882 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1883 Map(dout_server, gamedef),
1885 m_map_metadata_changed(true),
1887 m_database_read(NULL),
1888 m_database_write(NULL)
1890 verbosestream<<__FUNCTION_NAME<<std::endl;
1892 //m_chunksize = 8; // Takes a few seconds
1894 if (g_settings->get("fixed_map_seed").empty())
1896 m_seed = (((u64)(myrand()%0xffff)<<0)
1897 + ((u64)(myrand()%0xffff)<<16)
1898 + ((u64)(myrand()%0xffff)<<32)
1899 + ((u64)(myrand()%0xffff)<<48));
1903 m_seed = g_settings->getU64("fixed_map_seed");
1907 Experimental and debug stuff
1914 Try to load map; if not found, create a new one.
1917 m_savedir = savedir;
1918 m_map_saving_enabled = false;
1922 // If directory exists, check contents and load if possible
1923 if(fs::PathExists(m_savedir))
1925 // If directory is empty, it is safe to save into it.
1926 if(fs::GetDirListing(m_savedir).size() == 0)
1928 infostream<<"ServerMap: Empty save directory is valid."
1930 m_map_saving_enabled = true;
1935 // Load map metadata (seed, chunksize)
1938 catch(FileNotGoodException &e){
1939 infostream<<"WARNING: Could not load map metadata"
1940 //<<" Disabling chunk-based generator."
1945 infostream<<"ServerMap: Successfully loaded map "
1946 <<"metadata from "<<savedir
1947 <<", assuming valid save directory."
1948 <<" seed="<<m_seed<<"."
1951 m_map_saving_enabled = true;
1952 // Map loaded, not creating new one
1956 // If directory doesn't exist, it is safe to save to it
1958 m_map_saving_enabled = true;
1961 catch(std::exception &e)
1963 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
1964 <<", exception: "<<e.what()<<std::endl;
1965 infostream<<"Please remove the map or fix it."<<std::endl;
1966 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1969 infostream<<"Initializing new map."<<std::endl;
1971 // Create zero sector
1972 emergeSector(v2s16(0,0));
1974 // Initially write whole map
1975 save(MOD_STATE_CLEAN);
1978 ServerMap::~ServerMap()
1980 verbosestream<<__FUNCTION_NAME<<std::endl;
1984 if(m_map_saving_enabled)
1986 // Save only changed parts
1987 save(MOD_STATE_WRITE_AT_UNLOAD);
1988 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
1992 infostream<<"ServerMap: Map not saved"<<std::endl;
1995 catch(std::exception &e)
1997 infostream<<"ServerMap: Failed to save map to "<<m_savedir
1998 <<", exception: "<<e.what()<<std::endl;
2002 Close database if it was opened
2005 sqlite3_finalize(m_database_read);
2006 if(m_database_write)
2007 sqlite3_finalize(m_database_write);
2009 sqlite3_close(m_database);
2015 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2016 for(; i.atEnd() == false; i++)
2018 MapChunk *chunk = i.getNode()->getValue();
2024 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2026 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2027 if(enable_mapgen_debug_info)
2028 infostream<<"initBlockMake(): "
2029 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2030 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2033 //s16 chunksize = 3;
2034 //v3s16 chunk_offset(-1,-1,-1);
2035 //s16 chunksize = 4;
2036 //v3s16 chunk_offset(-1,-1,-1);
2038 v3s16 chunk_offset(-2,-2,-2);
2039 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2040 v3s16 blockpos_min = blockpos_div * chunksize;
2041 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2042 blockpos_min += chunk_offset;
2043 blockpos_max += chunk_offset;
2045 //v3s16 extra_borders(1,1,1);
2046 v3s16 extra_borders(1,1,1);
2048 // Do nothing if not inside limits (+-1 because of neighbors)
2049 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2050 blockpos_over_limit(blockpos_max + extra_borders))
2056 data->no_op = false;
2057 data->seed = m_seed;
2058 data->blockpos_min = blockpos_min;
2059 data->blockpos_max = blockpos_max;
2060 data->blockpos_requested = blockpos;
2061 data->nodedef = m_gamedef->ndef();
2064 Create the whole area of this and the neighboring blocks
2067 //TimeTaker timer("initBlockMake() create area");
2069 for(s16 x=blockpos_min.X-extra_borders.X;
2070 x<=blockpos_max.X+extra_borders.X; x++)
2071 for(s16 z=blockpos_min.Z-extra_borders.Z;
2072 z<=blockpos_max.Z+extra_borders.Z; z++)
2074 v2s16 sectorpos(x, z);
2075 // Sector metadata is loaded from disk if not already loaded.
2076 ServerMapSector *sector = createSector(sectorpos);
2079 for(s16 y=blockpos_min.Y-extra_borders.Y;
2080 y<=blockpos_max.Y+extra_borders.Y; y++)
2083 //MapBlock *block = createBlock(p);
2084 // 1) get from memory, 2) load from disk
2085 MapBlock *block = emergeBlock(p, false);
2086 // 3) create a blank one
2089 block = createBlock(p);
2092 Block gets sunlight if this is true.
2094 Refer to the map generator heuristics.
2096 bool ug = mapgen::block_is_underground(data->seed, p);
2097 block->setIsUnderground(ug);
2100 // Lighting will not be valid after make_chunk is called
2101 block->setLightingExpired(true);
2102 // Lighting will be calculated
2103 //block->setLightingExpired(false);
2109 Now we have a big empty area.
2111 Make a ManualMapVoxelManipulator that contains this and the
2115 // The area that contains this block and it's neighbors
2116 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2117 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2119 data->vmanip = new ManualMapVoxelManipulator(this);
2120 //data->vmanip->setMap(this);
2124 //TimeTaker timer("initBlockMake() initialEmerge");
2125 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2128 // Data is ready now.
2131 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2132 core::map<v3s16, MapBlock*> &changed_blocks)
2134 v3s16 blockpos_min = data->blockpos_min;
2135 v3s16 blockpos_max = data->blockpos_max;
2136 v3s16 blockpos_requested = data->blockpos_requested;
2137 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2138 <<blockpos_requested.Y<<","
2139 <<blockpos_requested.Z<<")"<<std::endl;*/
2141 v3s16 extra_borders(1,1,1);
2145 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2149 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2151 /*infostream<<"Resulting vmanip:"<<std::endl;
2152 data->vmanip.print(infostream);*/
2154 // Make sure affected blocks are loaded
2155 for(s16 x=blockpos_min.X-extra_borders.X;
2156 x<=blockpos_max.X+extra_borders.X; x++)
2157 for(s16 z=blockpos_min.Z-extra_borders.Z;
2158 z<=blockpos_max.Z+extra_borders.Z; z++)
2159 for(s16 y=blockpos_min.Y-extra_borders.Y;
2160 y<=blockpos_max.Y+extra_borders.Y; y++)
2163 // Load from disk if not already in memory
2164 emergeBlock(p, false);
2168 Blit generated stuff to map
2169 NOTE: blitBackAll adds nearly everything to changed_blocks
2173 //TimeTaker timer("finishBlockMake() blitBackAll");
2174 data->vmanip->blitBackAll(&changed_blocks);
2177 if(enable_mapgen_debug_info)
2178 infostream<<"finishBlockMake: changed_blocks.size()="
2179 <<changed_blocks.size()<<std::endl;
2182 Copy transforming liquid information
2184 while(data->transforming_liquid.size() > 0)
2186 v3s16 p = data->transforming_liquid.pop_front();
2187 m_transforming_liquid.push_back(p);
2191 Do stuff in central blocks
2199 TimeTaker t("finishBlockMake lighting update");
2201 core::map<v3s16, MapBlock*> lighting_update_blocks;
2204 for(s16 x=blockpos_min.X-extra_borders.X;
2205 x<=blockpos_max.X+extra_borders.X; x++)
2206 for(s16 z=blockpos_min.Z-extra_borders.Z;
2207 z<=blockpos_max.Z+extra_borders.Z; z++)
2208 for(s16 y=blockpos_min.Y-extra_borders.Y;
2209 y<=blockpos_max.Y+extra_borders.Y; y++)
2212 MapBlock *block = getBlockNoCreateNoEx(p);
2214 lighting_update_blocks.insert(block->getPos(), block);
2217 updateLighting(lighting_update_blocks, changed_blocks);
2221 Set lighting to non-expired state in all of them.
2222 This is cheating, but it is not fast enough if all of them
2223 would actually be updated.
2225 for(s16 x=blockpos_min.X-extra_borders.X;
2226 x<=blockpos_max.X+extra_borders.X; x++)
2227 for(s16 z=blockpos_min.Z-extra_borders.Z;
2228 z<=blockpos_max.Z+extra_borders.Z; z++)
2229 for(s16 y=blockpos_min.Y-extra_borders.Y;
2230 y<=blockpos_max.Y+extra_borders.Y; y++)
2233 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2237 if(enable_mapgen_debug_info == false)
2238 t.stop(true); // Hide output
2243 Go through changed blocks
2245 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2246 i.atEnd() == false; i++)
2248 MapBlock *block = i.getNode()->getValue();
2251 Update day/night difference cache of the MapBlocks
2253 block->expireDayNightDiff();
2255 Set block as modified
2257 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2258 "finishBlockMake expireDayNightDiff");
2262 Set central blocks as generated
2264 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2265 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2266 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2269 MapBlock *block = getBlockNoCreateNoEx(p);
2271 block->setGenerated(true);
2275 Save changed parts of map
2276 NOTE: Will be saved later.
2278 //save(MOD_STATE_WRITE_AT_UNLOAD);
2280 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2281 <<","<<blockpos_requested.Y<<","
2282 <<blockpos_requested.Z<<")"<<std::endl;*/
2284 if(enable_mapgen_debug_info)
2287 Analyze resulting blocks
2289 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2290 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2291 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2292 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2293 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2294 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2296 v3s16 p = v3s16(x,y,z);
2297 MapBlock *block = getBlockNoCreateNoEx(p);
2299 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2300 infostream<<"Generated "<<spos<<": "
2301 <<analyze_block(block)<<std::endl;
2306 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2312 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2314 DSTACKF("%s: p2d=(%d,%d)",
2319 Check if it exists already in memory
2321 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2326 Try to load it from disk (with blocks)
2328 //if(loadSectorFull(p2d) == true)
2331 Try to load metadata from disk
2334 if(loadSectorMeta(p2d) == true)
2336 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2339 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2340 throw InvalidPositionException("");
2346 Do not create over-limit
2348 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2349 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2350 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2351 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2352 throw InvalidPositionException("createSector(): pos. over limit");
2355 Generate blank sector
2358 sector = new ServerMapSector(this, p2d, m_gamedef);
2360 // Sector position on map in nodes
2361 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2366 m_sectors.insert(p2d, sector);
2372 This is a quick-hand function for calling makeBlock().
2374 MapBlock * ServerMap::generateBlock(
2376 core::map<v3s16, MapBlock*> &modified_blocks
2379 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2381 /*infostream<<"generateBlock(): "
2382 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2385 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2387 TimeTaker timer("generateBlock");
2389 //MapBlock *block = original_dummy;
2391 v2s16 p2d(p.X, p.Z);
2392 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2395 Do not generate over-limit
2397 if(blockpos_over_limit(p))
2399 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2400 throw InvalidPositionException("generateBlock(): pos. over limit");
2404 Create block make data
2406 mapgen::BlockMakeData data;
2407 initBlockMake(&data, p);
2413 TimeTaker t("mapgen::make_block()");
2414 mapgen::make_block(&data);
2416 if(enable_mapgen_debug_info == false)
2417 t.stop(true); // Hide output
2421 Blit data back on map, update lighting, add mobs and whatever this does
2423 finishBlockMake(&data, modified_blocks);
2428 MapBlock *block = getBlockNoCreateNoEx(p);
2436 bool erroneus_content = false;
2437 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2438 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2439 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2442 MapNode n = block->getNode(p);
2443 if(n.getContent() == CONTENT_IGNORE)
2445 infostream<<"CONTENT_IGNORE at "
2446 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2448 erroneus_content = true;
2452 if(erroneus_content)
2461 Generate a completely empty block
2465 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2466 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2468 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2471 n.setContent(CONTENT_AIR);
2472 block->setNode(v3s16(x0,y0,z0), n);
2478 if(enable_mapgen_debug_info == false)
2479 timer.stop(true); // Hide output
2484 MapBlock * ServerMap::createBlock(v3s16 p)
2486 DSTACKF("%s: p=(%d,%d,%d)",
2487 __FUNCTION_NAME, p.X, p.Y, p.Z);
2490 Do not create over-limit
2492 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2493 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2494 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2495 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2496 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2497 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2498 throw InvalidPositionException("createBlock(): pos. over limit");
2500 v2s16 p2d(p.X, p.Z);
2503 This will create or load a sector if not found in memory.
2504 If block exists on disk, it will be loaded.
2506 NOTE: On old save formats, this will be slow, as it generates
2507 lighting on blocks for them.
2509 ServerMapSector *sector;
2511 sector = (ServerMapSector*)createSector(p2d);
2512 assert(sector->getId() == MAPSECTOR_SERVER);
2514 catch(InvalidPositionException &e)
2516 infostream<<"createBlock: createSector() failed"<<std::endl;
2520 NOTE: This should not be done, or at least the exception
2521 should not be passed on as std::exception, because it
2522 won't be catched at all.
2524 /*catch(std::exception &e)
2526 infostream<<"createBlock: createSector() failed: "
2527 <<e.what()<<std::endl;
2532 Try to get a block from the sector
2535 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2538 if(block->isDummy())
2543 block = sector->createBlankBlock(block_y);
2547 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2549 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2551 p.X, p.Y, p.Z, allow_generate);
2554 MapBlock *block = getBlockNoCreateNoEx(p);
2555 if(block && block->isDummy() == false)
2560 MapBlock *block = loadBlock(p);
2567 core::map<v3s16, MapBlock*> modified_blocks;
2568 MapBlock *block = generateBlock(p, modified_blocks);
2572 event.type = MEET_OTHER;
2575 // Copy modified_blocks to event
2576 for(core::map<v3s16, MapBlock*>::Iterator
2577 i = modified_blocks.getIterator();
2578 i.atEnd()==false; i++)
2580 event.modified_blocks.insert(i.getNode()->getKey(), false);
2584 dispatchEvent(&event);
2593 s16 ServerMap::findGroundLevel(v2s16 p2d)
2597 Uh, just do something random...
2599 // Find existing map from top to down
2602 v3s16 p(p2d.X, max, p2d.Y);
2603 for(; p.Y>min; p.Y--)
2605 MapNode n = getNodeNoEx(p);
2606 if(n.getContent() != CONTENT_IGNORE)
2611 // If this node is not air, go to plan b
2612 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2614 // Search existing walkable and return it
2615 for(; p.Y>min; p.Y--)
2617 MapNode n = getNodeNoEx(p);
2618 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2627 Determine from map generator noise functions
2630 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2633 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2634 //return (s16)level;
2637 void ServerMap::createDatabase() {
2640 e = sqlite3_exec(m_database,
2641 "CREATE TABLE IF NOT EXISTS `blocks` ("
2642 "`pos` INT NOT NULL PRIMARY KEY,"
2645 , NULL, NULL, NULL);
2646 if(e == SQLITE_ABORT)
2647 throw FileNotGoodException("Could not create database structure");
2649 infostream<<"ServerMap: Database structure was created";
2652 void ServerMap::verifyDatabase() {
2657 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2658 bool needs_create = false;
2662 Open the database connection
2665 createDirs(m_savedir);
2667 if(!fs::PathExists(dbp))
2668 needs_create = true;
2670 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2671 if(d != SQLITE_OK) {
2672 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2673 throw FileNotGoodException("Cannot open database file");
2679 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2680 if(d != SQLITE_OK) {
2681 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2682 throw FileNotGoodException("Cannot prepare read statement");
2685 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2686 if(d != SQLITE_OK) {
2687 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2688 throw FileNotGoodException("Cannot prepare write statement");
2691 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2692 if(d != SQLITE_OK) {
2693 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2694 throw FileNotGoodException("Cannot prepare read statement");
2697 infostream<<"ServerMap: Database opened"<<std::endl;
2701 bool ServerMap::loadFromFolders() {
2702 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2707 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2708 return (sqlite3_int64)pos.Z*16777216 +
2709 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2712 void ServerMap::createDirs(std::string path)
2714 if(fs::CreateAllDirs(path) == false)
2716 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2717 <<"\""<<path<<"\""<<std::endl;
2718 throw BaseException("ServerMap failed to create directory");
2722 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2728 snprintf(cc, 9, "%.4x%.4x",
2729 (unsigned int)pos.X&0xffff,
2730 (unsigned int)pos.Y&0xffff);
2732 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2734 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2735 (unsigned int)pos.X&0xfff,
2736 (unsigned int)pos.Y&0xfff);
2738 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2744 v2s16 ServerMap::getSectorPos(std::string dirname)
2748 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2749 assert(spos != std::string::npos);
2750 if(dirname.size() - spos == 8)
2753 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2755 else if(dirname.size() - spos == 3)
2758 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2759 // Sign-extend the 12 bit values up to 16 bits...
2760 if(x&0x800) x|=0xF000;
2761 if(y&0x800) y|=0xF000;
2768 v2s16 pos((s16)x, (s16)y);
2772 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2774 v2s16 p2d = getSectorPos(sectordir);
2776 if(blockfile.size() != 4){
2777 throw InvalidFilenameException("Invalid block filename");
2780 int r = sscanf(blockfile.c_str(), "%4x", &y);
2782 throw InvalidFilenameException("Invalid block filename");
2783 return v3s16(p2d.X, y, p2d.Y);
2786 std::string ServerMap::getBlockFilename(v3s16 p)
2789 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2793 void ServerMap::save(ModifiedState save_level)
2795 DSTACK(__FUNCTION_NAME);
2796 if(m_map_saving_enabled == false)
2798 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2802 if(save_level == MOD_STATE_CLEAN)
2803 infostream<<"ServerMap: Saving whole map, this can take time."
2806 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2811 // Profile modified reasons
2812 Profiler modprofiler;
2814 u32 sector_meta_count = 0;
2815 u32 block_count = 0;
2816 u32 block_count_all = 0; // Number of blocks in memory
2818 // Don't do anything with sqlite unless something is really saved
2819 bool save_started = false;
2821 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2822 for(; i.atEnd() == false; i++)
2824 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2825 assert(sector->getId() == MAPSECTOR_SERVER);
2827 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2829 saveSectorMeta(sector);
2830 sector_meta_count++;
2832 core::list<MapBlock*> blocks;
2833 sector->getBlocks(blocks);
2834 core::list<MapBlock*>::Iterator j;
2836 for(j=blocks.begin(); j!=blocks.end(); j++)
2838 MapBlock *block = *j;
2842 if(block->getModified() >= save_level)
2847 save_started = true;
2850 modprofiler.add(block->getModifiedReason(), 1);
2855 /*infostream<<"ServerMap: Written block ("
2856 <<block->getPos().X<<","
2857 <<block->getPos().Y<<","
2858 <<block->getPos().Z<<")"
2867 Only print if something happened or saved whole map
2869 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2870 || block_count != 0)
2872 infostream<<"ServerMap: Written: "
2873 <<sector_meta_count<<" sector metadata files, "
2874 <<block_count<<" block files"
2875 <<", "<<block_count_all<<" blocks in memory."
2877 PrintInfo(infostream); // ServerMap/ClientMap:
2878 infostream<<"Blocks modified by: "<<std::endl;
2879 modprofiler.print(infostream);
2883 static s32 unsignedToSigned(s32 i, s32 max_positive)
2885 if(i < max_positive)
2888 return i - 2*max_positive;
2891 // modulo of a negative number does not work consistently in C
2892 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2896 return mod - ((-i) % mod);
2899 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2901 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2903 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2905 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2906 return v3s16(x,y,z);
2909 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2911 if(loadFromFolders()){
2912 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2913 <<"all blocks that are stored in flat files"<<std::endl;
2919 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2921 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2922 v3s16 p = getIntegerAsBlock(block_i);
2923 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2929 void ServerMap::saveMapMeta()
2931 DSTACK(__FUNCTION_NAME);
2933 /*infostream<<"ServerMap::saveMapMeta(): "
2937 createDirs(m_savedir);
2939 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2940 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2941 if(os.good() == false)
2943 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2944 <<"could not open"<<fullpath<<std::endl;
2945 throw FileNotGoodException("Cannot open chunk metadata");
2949 params.setU64("seed", m_seed);
2951 params.writeLines(os);
2953 os<<"[end_of_params]\n";
2955 m_map_metadata_changed = false;
2958 void ServerMap::loadMapMeta()
2960 DSTACK(__FUNCTION_NAME);
2962 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2965 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2966 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2967 if(is.good() == false)
2969 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2970 <<"could not open"<<fullpath<<std::endl;
2971 throw FileNotGoodException("Cannot open map metadata");
2979 throw SerializationError
2980 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2982 std::getline(is, line);
2983 std::string trimmedline = trim(line);
2984 if(trimmedline == "[end_of_params]")
2986 params.parseConfigLine(line);
2989 m_seed = params.getU64("seed");
2991 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
2994 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2996 DSTACK(__FUNCTION_NAME);
2997 // Format used for writing
2998 u8 version = SER_FMT_VER_HIGHEST;
3000 v2s16 pos = sector->getPos();
3001 std::string dir = getSectorDir(pos);
3004 std::string fullpath = dir + DIR_DELIM + "meta";
3005 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3006 if(o.good() == false)
3007 throw FileNotGoodException("Cannot open sector metafile");
3009 sector->serialize(o, version);
3011 sector->differs_from_disk = false;
3014 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3016 DSTACK(__FUNCTION_NAME);
3018 v2s16 p2d = getSectorPos(sectordir);
3020 ServerMapSector *sector = NULL;
3022 std::string fullpath = sectordir + DIR_DELIM + "meta";
3023 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3024 if(is.good() == false)
3026 // If the directory exists anyway, it probably is in some old
3027 // format. Just go ahead and create the sector.
3028 if(fs::PathExists(sectordir))
3030 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3031 <<fullpath<<" doesn't exist but directory does."
3032 <<" Continuing with a sector with no metadata."
3034 sector = new ServerMapSector(this, p2d, m_gamedef);
3035 m_sectors.insert(p2d, sector);
3039 throw FileNotGoodException("Cannot open sector metafile");
3044 sector = ServerMapSector::deSerialize
3045 (is, this, p2d, m_sectors, m_gamedef);
3047 saveSectorMeta(sector);
3050 sector->differs_from_disk = false;
3055 bool ServerMap::loadSectorMeta(v2s16 p2d)
3057 DSTACK(__FUNCTION_NAME);
3059 MapSector *sector = NULL;
3061 // The directory layout we're going to load from.
3062 // 1 - original sectors/xxxxzzzz/
3063 // 2 - new sectors2/xxx/zzz/
3064 // If we load from anything but the latest structure, we will
3065 // immediately save to the new one, and remove the old.
3067 std::string sectordir1 = getSectorDir(p2d, 1);
3068 std::string sectordir;
3069 if(fs::PathExists(sectordir1))
3071 sectordir = sectordir1;
3076 sectordir = getSectorDir(p2d, 2);
3080 sector = loadSectorMeta(sectordir, loadlayout != 2);
3082 catch(InvalidFilenameException &e)
3086 catch(FileNotGoodException &e)
3090 catch(std::exception &e)
3099 bool ServerMap::loadSectorFull(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)
3142 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3144 std::vector<fs::DirListNode>::iterator i2;
3145 for(i2=list2.begin(); i2!=list2.end(); i2++)
3151 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3153 catch(InvalidFilenameException &e)
3155 // This catches unknown crap in directory
3161 infostream<<"Sector converted to new layout - deleting "<<
3162 sectordir1<<std::endl;
3163 fs::RecursiveDelete(sectordir1);
3170 void ServerMap::beginSave() {
3172 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3173 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3176 void ServerMap::endSave() {
3178 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3179 infostream<<"WARNING: endSave() failed, map might not have saved.";
3182 void ServerMap::saveBlock(MapBlock *block)
3184 DSTACK(__FUNCTION_NAME);
3186 Dummy blocks are not written
3188 if(block->isDummy())
3190 /*v3s16 p = block->getPos();
3191 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3192 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3196 // Format used for writing
3197 u8 version = SER_FMT_VER_HIGHEST;
3199 v3s16 p3d = block->getPos();
3203 v2s16 p2d(p3d.X, p3d.Z);
3204 std::string sectordir = getSectorDir(p2d);
3206 createDirs(sectordir);
3208 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3209 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3210 if(o.good() == false)
3211 throw FileNotGoodException("Cannot open block data");
3214 [0] u8 serialization version
3220 std::ostringstream o(std::ios_base::binary);
3222 o.write((char*)&version, 1);
3225 block->serialize(o, version, true);
3227 // Write block to database
3229 std::string tmp = o.str();
3230 const char *bytes = tmp.c_str();
3232 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3233 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3234 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3235 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3236 int written = sqlite3_step(m_database_write);
3237 if(written != SQLITE_DONE)
3238 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3239 <<sqlite3_errmsg(m_database)<<std::endl;
3240 // Make ready for later reuse
3241 sqlite3_reset(m_database_write);
3243 // We just wrote it to the disk so clear modified flag
3244 block->resetModified();
3247 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3249 DSTACK(__FUNCTION_NAME);
3251 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3254 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3255 if(is.good() == false)
3256 throw FileNotGoodException("Cannot open block file");
3258 v3s16 p3d = getBlockPos(sectordir, blockfile);
3259 v2s16 p2d(p3d.X, p3d.Z);
3261 assert(sector->getPos() == p2d);
3263 u8 version = SER_FMT_VER_INVALID;
3264 is.read((char*)&version, 1);
3267 throw SerializationError("ServerMap::loadBlock(): Failed"
3268 " to read MapBlock version");
3270 /*u32 block_size = MapBlock::serializedLength(version);
3271 SharedBuffer<u8> data(block_size);
3272 is.read((char*)*data, block_size);*/
3274 // This will always return a sector because we're the server
3275 //MapSector *sector = emergeSector(p2d);
3277 MapBlock *block = NULL;
3278 bool created_new = false;
3279 block = sector->getBlockNoCreateNoEx(p3d.Y);
3282 block = sector->createBlankBlockNoInsert(p3d.Y);
3287 block->deSerialize(is, version, true);
3289 // If it's a new block, insert it to the map
3291 sector->insertBlock(block);
3294 Save blocks loaded in old format in new format
3297 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3301 // Should be in database now, so delete the old file
3302 fs::RecursiveDelete(fullpath);
3305 // We just loaded it from the disk, so it's up-to-date.
3306 block->resetModified();
3309 catch(SerializationError &e)
3311 infostream<<"WARNING: Invalid block data on disk "
3312 <<"fullpath="<<fullpath
3313 <<" (SerializationError). "
3314 <<"what()="<<e.what()
3316 //" Ignoring. A new one will be generated.
3319 // TODO: Backup file; name is in fullpath.
3323 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3325 DSTACK(__FUNCTION_NAME);
3328 std::istringstream is(*blob, std::ios_base::binary);
3330 u8 version = SER_FMT_VER_INVALID;
3331 is.read((char*)&version, 1);
3334 throw SerializationError("ServerMap::loadBlock(): Failed"
3335 " to read MapBlock version");
3337 /*u32 block_size = MapBlock::serializedLength(version);
3338 SharedBuffer<u8> data(block_size);
3339 is.read((char*)*data, block_size);*/
3341 // This will always return a sector because we're the server
3342 //MapSector *sector = emergeSector(p2d);
3344 MapBlock *block = NULL;
3345 bool created_new = false;
3346 block = sector->getBlockNoCreateNoEx(p3d.Y);
3349 block = sector->createBlankBlockNoInsert(p3d.Y);
3354 block->deSerialize(is, version, true);
3356 // If it's a new block, insert it to the map
3358 sector->insertBlock(block);
3361 Save blocks loaded in old format in new format
3364 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3365 // Only save if asked to; no need to update version
3369 // We just loaded it from, so it's up-to-date.
3370 block->resetModified();
3373 catch(SerializationError &e)
3375 infostream<<"WARNING: Invalid block data in database "
3376 <<" (SerializationError). "
3377 <<"what()="<<e.what()
3379 //" Ignoring. A new one will be generated.
3382 // TODO: Copy to a backup database.
3386 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3388 DSTACK(__FUNCTION_NAME);
3390 v2s16 p2d(blockpos.X, blockpos.Z);
3392 if(!loadFromFolders()) {
3395 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3396 infostream<<"WARNING: Could not bind block position for load: "
3397 <<sqlite3_errmsg(m_database)<<std::endl;
3398 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3400 Make sure sector is loaded
3402 MapSector *sector = createSector(p2d);
3407 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3408 size_t len = sqlite3_column_bytes(m_database_read, 0);
3410 std::string datastr(data, len);
3412 loadBlock(&datastr, blockpos, sector, false);
3414 sqlite3_step(m_database_read);
3415 // We should never get more than 1 row, so ok to reset
3416 sqlite3_reset(m_database_read);
3418 return getBlockNoCreateNoEx(blockpos);
3420 sqlite3_reset(m_database_read);
3422 // Not found in database, try the files
3425 // The directory layout we're going to load from.
3426 // 1 - original sectors/xxxxzzzz/
3427 // 2 - new sectors2/xxx/zzz/
3428 // If we load from anything but the latest structure, we will
3429 // immediately save to the new one, and remove the old.
3431 std::string sectordir1 = getSectorDir(p2d, 1);
3432 std::string sectordir;
3433 if(fs::PathExists(sectordir1))
3435 sectordir = sectordir1;
3440 sectordir = getSectorDir(p2d, 2);
3444 Make sure sector is loaded
3446 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3450 sector = loadSectorMeta(sectordir, loadlayout != 2);
3452 catch(InvalidFilenameException &e)
3456 catch(FileNotGoodException &e)
3460 catch(std::exception &e)
3467 Make sure file exists
3470 std::string blockfilename = getBlockFilename(blockpos);
3471 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3475 Load block and save it to the database
3477 loadBlock(sectordir, blockfilename, sector, true);
3478 return getBlockNoCreateNoEx(blockpos);
3481 void ServerMap::PrintInfo(std::ostream &out)
3490 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3495 MapVoxelManipulator::~MapVoxelManipulator()
3497 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3501 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3503 TimeTaker timer1("emerge", &emerge_time);
3505 // Units of these are MapBlocks
3506 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3507 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3509 VoxelArea block_area_nodes
3510 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3512 addArea(block_area_nodes);
3514 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3515 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3516 for(s32 x=p_min.X; x<=p_max.X; x++)
3519 core::map<v3s16, bool>::Node *n;
3520 n = m_loaded_blocks.find(p);
3524 bool block_data_inexistent = false;
3527 TimeTaker timer1("emerge load", &emerge_load_time);
3529 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3530 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3532 a.print(infostream);
3533 infostream<<std::endl;*/
3535 MapBlock *block = m_map->getBlockNoCreate(p);
3536 if(block->isDummy())
3537 block_data_inexistent = true;
3539 block->copyTo(*this);
3541 catch(InvalidPositionException &e)
3543 block_data_inexistent = true;
3546 if(block_data_inexistent)
3548 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3549 // Fill with VOXELFLAG_INEXISTENT
3550 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3551 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3553 s32 i = m_area.index(a.MinEdge.X,y,z);
3554 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3558 m_loaded_blocks.insert(p, !block_data_inexistent);
3561 //infostream<<"emerge done"<<std::endl;
3565 SUGG: Add an option to only update eg. water and air nodes.
3566 This will make it interfere less with important stuff if
3569 void MapVoxelManipulator::blitBack
3570 (core::map<v3s16, MapBlock*> & modified_blocks)
3572 if(m_area.getExtent() == v3s16(0,0,0))
3575 //TimeTaker timer1("blitBack");
3577 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3578 <<m_loaded_blocks.size()<<std::endl;*/
3581 Initialize block cache
3583 v3s16 blockpos_last;
3584 MapBlock *block = NULL;
3585 bool block_checked_in_modified = false;
3587 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3588 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3589 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3593 u8 f = m_flags[m_area.index(p)];
3594 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3597 MapNode &n = m_data[m_area.index(p)];
3599 v3s16 blockpos = getNodeBlockPos(p);
3604 if(block == NULL || blockpos != blockpos_last){
3605 block = m_map->getBlockNoCreate(blockpos);
3606 blockpos_last = blockpos;
3607 block_checked_in_modified = false;
3610 // Calculate relative position in block
3611 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3613 // Don't continue if nothing has changed here
3614 if(block->getNode(relpos) == n)
3617 //m_map->setNode(m_area.MinEdge + p, n);
3618 block->setNode(relpos, n);
3621 Make sure block is in modified_blocks
3623 if(block_checked_in_modified == false)
3625 modified_blocks[blockpos] = block;
3626 block_checked_in_modified = true;
3629 catch(InvalidPositionException &e)
3635 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3636 MapVoxelManipulator(map),
3637 m_create_area(false)
3641 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3645 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3647 // Just create the area so that it can be pointed to
3648 VoxelManipulator::emerge(a, caller_id);
3651 void ManualMapVoxelManipulator::initialEmerge(
3652 v3s16 blockpos_min, v3s16 blockpos_max)
3654 TimeTaker timer1("initialEmerge", &emerge_time);
3656 // Units of these are MapBlocks
3657 v3s16 p_min = blockpos_min;
3658 v3s16 p_max = blockpos_max;
3660 VoxelArea block_area_nodes
3661 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3663 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3666 infostream<<"initialEmerge: area: ";
3667 block_area_nodes.print(infostream);
3668 infostream<<" ("<<size_MB<<"MB)";
3669 infostream<<std::endl;
3672 addArea(block_area_nodes);
3674 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3675 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3676 for(s32 x=p_min.X; x<=p_max.X; x++)
3679 core::map<v3s16, bool>::Node *n;
3680 n = m_loaded_blocks.find(p);
3684 bool block_data_inexistent = false;
3687 TimeTaker timer1("emerge load", &emerge_load_time);
3689 MapBlock *block = m_map->getBlockNoCreate(p);
3690 if(block->isDummy())
3691 block_data_inexistent = true;
3693 block->copyTo(*this);
3695 catch(InvalidPositionException &e)
3697 block_data_inexistent = true;
3700 if(block_data_inexistent)
3703 Mark area inexistent
3705 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3706 // Fill with VOXELFLAG_INEXISTENT
3707 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3708 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3710 s32 i = m_area.index(a.MinEdge.X,y,z);
3711 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3715 m_loaded_blocks.insert(p, !block_data_inexistent);
3719 void ManualMapVoxelManipulator::blitBackAll(
3720 core::map<v3s16, MapBlock*> * modified_blocks)
3722 if(m_area.getExtent() == v3s16(0,0,0))
3726 Copy data of all blocks
3728 for(core::map<v3s16, bool>::Iterator
3729 i = m_loaded_blocks.getIterator();
3730 i.atEnd() == false; i++)
3732 v3s16 p = i.getNode()->getKey();
3733 bool existed = i.getNode()->getValue();
3734 if(existed == false)
3736 // The Great Bug was found using this
3737 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3738 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3742 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3745 infostream<<"WARNING: "<<__FUNCTION_NAME
3746 <<": got NULL block "
3747 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3752 block->copyFrom(*this);
3755 modified_blocks->insert(p, block);