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"
35 #include "rollback_interface.h"
36 #include "mapgen_v6.h"
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
41 SQLite format specification:
42 - Initially only replaces sectors/ and sectors2/
44 If map.sqlite does not exist in the save dir
45 or the block was not found in the database
46 the map will try to load from sectors folder.
47 In either case, map.sqlite will be created
48 and all future saves will save there.
50 Structure of map.sqlite:
61 Map::Map(std::ostream &dout, IGameDef *gamedef):
66 /*m_sector_mutex.Init();
67 assert(m_sector_mutex.IsInitialized());*/
75 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
76 for(; i.atEnd() == false; i++)
78 MapSector *sector = i.getNode()->getValue();
83 void Map::addEventReceiver(MapEventReceiver *event_receiver)
85 m_event_receivers.insert(event_receiver, false);
88 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
90 if(m_event_receivers.find(event_receiver) == NULL)
92 m_event_receivers.remove(event_receiver);
95 void Map::dispatchEvent(MapEditEvent *event)
97 for(core::map<MapEventReceiver*, bool>::Iterator
98 i = m_event_receivers.getIterator();
99 i.atEnd()==false; i++)
101 MapEventReceiver* event_receiver = i.getNode()->getKey();
102 event_receiver->onMapEditEvent(event);
106 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
108 if(m_sector_cache != NULL && p == m_sector_cache_p){
109 MapSector * sector = m_sector_cache;
113 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
118 MapSector *sector = n->getValue();
120 // Cache the last result
121 m_sector_cache_p = p;
122 m_sector_cache = sector;
127 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
129 return getSectorNoGenerateNoExNoLock(p);
132 MapSector * Map::getSectorNoGenerate(v2s16 p)
134 MapSector *sector = getSectorNoGenerateNoEx(p);
136 throw InvalidPositionException();
141 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
143 v2s16 p2d(p3d.X, p3d.Z);
144 MapSector * sector = getSectorNoGenerateNoEx(p2d);
147 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
151 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
153 MapBlock *block = getBlockNoCreateNoEx(p3d);
155 throw InvalidPositionException();
159 bool Map::isNodeUnderground(v3s16 p)
161 v3s16 blockpos = getNodeBlockPos(p);
163 MapBlock * block = getBlockNoCreate(blockpos);
164 return block->getIsUnderground();
166 catch(InvalidPositionException &e)
172 bool Map::isValidPosition(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreate(blockpos);
176 return (block != NULL);
179 // Returns a CONTENT_IGNORE node if not found
180 MapNode Map::getNodeNoEx(v3s16 p)
182 v3s16 blockpos = getNodeBlockPos(p);
183 MapBlock *block = getBlockNoCreateNoEx(blockpos);
185 return MapNode(CONTENT_IGNORE);
186 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
187 return block->getNodeNoCheck(relpos);
190 // throws InvalidPositionException if not found
191 MapNode Map::getNode(v3s16 p)
193 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock *block = getBlockNoCreateNoEx(blockpos);
196 throw InvalidPositionException();
197 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
198 return block->getNodeNoCheck(relpos);
201 // throws InvalidPositionException if not found
202 void Map::setNode(v3s16 p, MapNode & n)
204 v3s16 blockpos = getNodeBlockPos(p);
205 MapBlock *block = getBlockNoCreate(blockpos);
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 // Never allow placing CONTENT_IGNORE, it fucks up stuff
208 if(n.getContent() == CONTENT_IGNORE){
209 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
210 <<" while trying to replace \""
211 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
212 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
213 debug_stacks_print_to(infostream);
216 block->setNodeNoCheck(relpos, n);
221 Goes recursively through the neighbours of the node.
223 Alters only transparent nodes.
225 If the lighting of the neighbour is lower than the lighting of
226 the node was (before changing it to 0 at the step before), the
227 lighting of the neighbour is set to 0 and then the same stuff
228 repeats for the neighbour.
230 The ending nodes of the routine are stored in light_sources.
231 This is useful when a light is removed. In such case, this
232 routine can be called for the light node and then again for
233 light_sources to re-light the area without the removed light.
235 values of from_nodes are lighting values.
237 void Map::unspreadLight(enum LightBank bank,
238 core::map<v3s16, u8> & from_nodes,
239 core::map<v3s16, bool> & light_sources,
240 core::map<v3s16, MapBlock*> & modified_blocks)
242 INodeDefManager *nodemgr = m_gamedef->ndef();
245 v3s16(0,0,1), // back
247 v3s16(1,0,0), // right
248 v3s16(0,0,-1), // front
249 v3s16(0,-1,0), // bottom
250 v3s16(-1,0,0), // left
253 if(from_nodes.size() == 0)
256 u32 blockchangecount = 0;
258 core::map<v3s16, u8> unlighted_nodes;
259 core::map<v3s16, u8>::Iterator j;
260 j = from_nodes.getIterator();
263 Initialize block cache
266 MapBlock *block = NULL;
267 // Cache this a bit, too
268 bool block_checked_in_modified = false;
270 for(; j.atEnd() == false; j++)
272 v3s16 pos = j.getNode()->getKey();
273 v3s16 blockpos = getNodeBlockPos(pos);
275 // Only fetch a new block if the block position has changed
277 if(block == NULL || blockpos != blockpos_last){
278 block = getBlockNoCreate(blockpos);
279 blockpos_last = blockpos;
281 block_checked_in_modified = false;
285 catch(InvalidPositionException &e)
293 // Calculate relative position in block
294 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
296 // Get node straight from the block
297 MapNode n = block->getNode(relpos);
299 u8 oldlight = j.getNode()->getValue();
301 // Loop through 6 neighbors
302 for(u16 i=0; i<6; i++)
304 // Get the position of the neighbor node
305 v3s16 n2pos = pos + dirs[i];
307 // Get the block where the node is located
308 v3s16 blockpos = getNodeBlockPos(n2pos);
312 // Only fetch a new block if the block position has changed
314 if(block == NULL || blockpos != blockpos_last){
315 block = getBlockNoCreate(blockpos);
316 blockpos_last = blockpos;
318 block_checked_in_modified = false;
322 catch(InvalidPositionException &e)
327 // Calculate relative position in block
328 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
329 // Get node straight from the block
330 MapNode n2 = block->getNode(relpos);
332 bool changed = false;
334 //TODO: Optimize output by optimizing light_sources?
337 If the neighbor is dimmer than what was specified
338 as oldlight (the light of the previous node)
340 if(n2.getLight(bank, nodemgr) < oldlight)
343 And the neighbor is transparent and it has some light
345 if(nodemgr->get(n2).light_propagates
346 && n2.getLight(bank, nodemgr) != 0)
349 Set light to 0 and add to queue
352 u8 current_light = n2.getLight(bank, nodemgr);
353 n2.setLight(bank, 0, nodemgr);
354 block->setNode(relpos, n2);
356 unlighted_nodes.insert(n2pos, current_light);
360 Remove from light_sources if it is there
361 NOTE: This doesn't happen nearly at all
363 /*if(light_sources.find(n2pos))
365 infostream<<"Removed from light_sources"<<std::endl;
366 light_sources.remove(n2pos);
371 if(light_sources.find(n2pos) != NULL)
372 light_sources.remove(n2pos);*/
375 light_sources.insert(n2pos, true);
378 // Add to modified_blocks
379 if(changed == true && block_checked_in_modified == false)
381 // If the block is not found in modified_blocks, add.
382 if(modified_blocks.find(blockpos) == NULL)
384 modified_blocks.insert(blockpos, block);
386 block_checked_in_modified = true;
389 catch(InvalidPositionException &e)
396 /*infostream<<"unspreadLight(): Changed block "
397 <<blockchangecount<<" times"
398 <<" for "<<from_nodes.size()<<" nodes"
401 if(unlighted_nodes.size() > 0)
402 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
406 A single-node wrapper of the above
408 void Map::unLightNeighbors(enum LightBank bank,
409 v3s16 pos, u8 lightwas,
410 core::map<v3s16, bool> & light_sources,
411 core::map<v3s16, MapBlock*> & modified_blocks)
413 core::map<v3s16, u8> from_nodes;
414 from_nodes.insert(pos, lightwas);
416 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
420 Lights neighbors of from_nodes, collects all them and then
423 void Map::spreadLight(enum LightBank bank,
424 core::map<v3s16, bool> & from_nodes,
425 core::map<v3s16, MapBlock*> & modified_blocks)
427 INodeDefManager *nodemgr = m_gamedef->ndef();
429 const v3s16 dirs[6] = {
430 v3s16(0,0,1), // back
432 v3s16(1,0,0), // right
433 v3s16(0,0,-1), // front
434 v3s16(0,-1,0), // bottom
435 v3s16(-1,0,0), // left
438 if(from_nodes.size() == 0)
441 u32 blockchangecount = 0;
443 core::map<v3s16, bool> lighted_nodes;
444 core::map<v3s16, bool>::Iterator j;
445 j = from_nodes.getIterator();
448 Initialize block cache
451 MapBlock *block = NULL;
452 // Cache this a bit, too
453 bool block_checked_in_modified = false;
455 for(; j.atEnd() == false; j++)
456 //for(; j != from_nodes.end(); j++)
458 v3s16 pos = j.getNode()->getKey();
460 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
461 v3s16 blockpos = getNodeBlockPos(pos);
463 // Only fetch a new block if the block position has changed
465 if(block == NULL || blockpos != blockpos_last){
466 block = getBlockNoCreate(blockpos);
467 blockpos_last = blockpos;
469 block_checked_in_modified = false;
473 catch(InvalidPositionException &e)
481 // Calculate relative position in block
482 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
484 // Get node straight from the block
485 MapNode n = block->getNode(relpos);
487 u8 oldlight = n.getLight(bank, nodemgr);
488 u8 newlight = diminish_light(oldlight);
490 // Loop through 6 neighbors
491 for(u16 i=0; i<6; i++){
492 // Get the position of the neighbor node
493 v3s16 n2pos = pos + dirs[i];
495 // Get the block where the node is located
496 v3s16 blockpos = getNodeBlockPos(n2pos);
500 // Only fetch a new block if the block position has changed
502 if(block == NULL || blockpos != blockpos_last){
503 block = getBlockNoCreate(blockpos);
504 blockpos_last = blockpos;
506 block_checked_in_modified = false;
510 catch(InvalidPositionException &e)
515 // Calculate relative position in block
516 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
517 // Get node straight from the block
518 MapNode n2 = block->getNode(relpos);
520 bool changed = false;
522 If the neighbor is brighter than the current node,
523 add to list (it will light up this node on its turn)
525 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
527 lighted_nodes.insert(n2pos, true);
528 //lighted_nodes.push_back(n2pos);
532 If the neighbor is dimmer than how much light this node
533 would spread on it, add to list
535 if(n2.getLight(bank, nodemgr) < newlight)
537 if(nodemgr->get(n2).light_propagates)
539 n2.setLight(bank, newlight, nodemgr);
540 block->setNode(relpos, n2);
541 lighted_nodes.insert(n2pos, true);
542 //lighted_nodes.push_back(n2pos);
547 // Add to modified_blocks
548 if(changed == true && block_checked_in_modified == false)
550 // If the block is not found in modified_blocks, add.
551 if(modified_blocks.find(blockpos) == NULL)
553 modified_blocks.insert(blockpos, block);
555 block_checked_in_modified = true;
558 catch(InvalidPositionException &e)
565 /*infostream<<"spreadLight(): Changed block "
566 <<blockchangecount<<" times"
567 <<" for "<<from_nodes.size()<<" nodes"
570 if(lighted_nodes.size() > 0)
571 spreadLight(bank, lighted_nodes, modified_blocks);
575 A single-node source variation of the above.
577 void Map::lightNeighbors(enum LightBank bank,
579 core::map<v3s16, MapBlock*> & modified_blocks)
581 core::map<v3s16, bool> from_nodes;
582 from_nodes.insert(pos, true);
583 spreadLight(bank, from_nodes, modified_blocks);
586 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
588 INodeDefManager *nodemgr = m_gamedef->ndef();
591 v3s16(0,0,1), // back
593 v3s16(1,0,0), // right
594 v3s16(0,0,-1), // front
595 v3s16(0,-1,0), // bottom
596 v3s16(-1,0,0), // left
599 u8 brightest_light = 0;
600 v3s16 brightest_pos(0,0,0);
601 bool found_something = false;
603 // Loop through 6 neighbors
604 for(u16 i=0; i<6; i++){
605 // Get the position of the neighbor node
606 v3s16 n2pos = p + dirs[i];
611 catch(InvalidPositionException &e)
615 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
616 brightest_light = n2.getLight(bank, nodemgr);
617 brightest_pos = n2pos;
618 found_something = true;
622 if(found_something == false)
623 throw InvalidPositionException();
625 return brightest_pos;
629 Propagates sunlight down from a node.
630 Starting point gets sunlight.
632 Returns the lowest y value of where the sunlight went.
634 Mud is turned into grass in where the sunlight stops.
636 s16 Map::propagateSunlight(v3s16 start,
637 core::map<v3s16, MapBlock*> & modified_blocks)
639 INodeDefManager *nodemgr = m_gamedef->ndef();
644 v3s16 pos(start.X, y, start.Z);
646 v3s16 blockpos = getNodeBlockPos(pos);
649 block = getBlockNoCreate(blockpos);
651 catch(InvalidPositionException &e)
656 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
657 MapNode n = block->getNode(relpos);
659 if(nodemgr->get(n).sunlight_propagates)
661 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
662 block->setNode(relpos, n);
664 modified_blocks.insert(blockpos, block);
668 // Sunlight goes no further
675 void Map::updateLighting(enum LightBank bank,
676 core::map<v3s16, MapBlock*> & a_blocks,
677 core::map<v3s16, MapBlock*> & modified_blocks)
679 INodeDefManager *nodemgr = m_gamedef->ndef();
681 /*m_dout<<DTIME<<"Map::updateLighting(): "
682 <<a_blocks.size()<<" blocks."<<std::endl;*/
684 //TimeTaker timer("updateLighting");
688 //u32 count_was = modified_blocks.size();
690 core::map<v3s16, MapBlock*> blocks_to_update;
692 core::map<v3s16, bool> light_sources;
694 core::map<v3s16, u8> unlight_from;
696 int num_bottom_invalid = 0;
699 //TimeTaker t("first stuff");
701 core::map<v3s16, MapBlock*>::Iterator i;
702 i = a_blocks.getIterator();
703 for(; i.atEnd() == false; i++)
705 MapBlock *block = i.getNode()->getValue();
709 // Don't bother with dummy blocks.
713 v3s16 pos = block->getPos();
714 v3s16 posnodes = block->getPosRelative();
715 modified_blocks.insert(pos, block);
717 blocks_to_update.insert(pos, block);
720 Clear all light from block
722 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
723 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
724 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
729 MapNode n = block->getNode(p);
730 u8 oldlight = n.getLight(bank, nodemgr);
731 n.setLight(bank, 0, nodemgr);
732 block->setNode(p, n);
734 // If node sources light, add to list
735 u8 source = nodemgr->get(n).light_source;
737 light_sources[p + posnodes] = true;
739 // Collect borders for unlighting
740 if((x==0 || x == MAP_BLOCKSIZE-1
741 || y==0 || y == MAP_BLOCKSIZE-1
742 || z==0 || z == MAP_BLOCKSIZE-1)
745 v3s16 p_map = p + posnodes;
746 unlight_from.insert(p_map, oldlight);
749 catch(InvalidPositionException &e)
752 This would happen when dealing with a
756 infostream<<"updateLighting(): InvalidPositionException"
761 if(bank == LIGHTBANK_DAY)
763 bool bottom_valid = block->propagateSunlight(light_sources);
766 num_bottom_invalid++;
768 // If bottom is valid, we're done.
772 else if(bank == LIGHTBANK_NIGHT)
774 // For night lighting, sunlight is not propagated
779 // Invalid lighting bank
783 /*infostream<<"Bottom for sunlight-propagated block ("
784 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
787 // Bottom sunlight is not valid; get the block and loop to it
791 block = getBlockNoCreate(pos);
793 catch(InvalidPositionException &e)
804 Enable this to disable proper lighting for speeding up map
805 generation for testing or whatever
808 //if(g_settings->get(""))
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
816 block->setLightingExpired(false);
824 //TimeTaker timer("unspreadLight");
825 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 infostream<<"unspreadLight modified "<<diff<<std::endl;
836 //TimeTaker timer("spreadLight");
837 spreadLight(bank, light_sources, modified_blocks);
842 u32 diff = modified_blocks.size() - count_was;
843 count_was = modified_blocks.size();
844 infostream<<"spreadLight modified "<<diff<<std::endl;
850 //MapVoxelManipulator vmanip(this);
852 // Make a manual voxel manipulator and load all the blocks
853 // that touch the requested blocks
854 ManualMapVoxelManipulator vmanip(this);
857 //TimeTaker timer("initialEmerge");
859 core::map<v3s16, MapBlock*>::Iterator i;
860 i = blocks_to_update.getIterator();
861 for(; i.atEnd() == false; i++)
863 MapBlock *block = i.getNode()->getValue();
864 v3s16 p = block->getPos();
866 // Add all surrounding blocks
867 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
870 Add all surrounding blocks that have up-to-date lighting
871 NOTE: This doesn't quite do the job (not everything
872 appropriate is lighted)
874 /*for(s16 z=-1; z<=1; z++)
875 for(s16 y=-1; y<=1; y++)
876 for(s16 x=-1; x<=1; x++)
878 v3s16 p2 = p + v3s16(x,y,z);
879 MapBlock *block = getBlockNoCreateNoEx(p2);
884 if(block->getLightingExpired())
886 vmanip.initialEmerge(p2, p2);
889 // Lighting of block will be updated completely
890 block->setLightingExpired(false);
895 //TimeTaker timer("unSpreadLight");
896 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
899 //TimeTaker timer("spreadLight");
900 vmanip.spreadLight(bank, light_sources, nodemgr);
903 //TimeTaker timer("blitBack");
904 vmanip.blitBack(modified_blocks);
906 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
911 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
914 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
915 core::map<v3s16, MapBlock*> & modified_blocks)
917 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
918 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
921 Update information about whether day and night light differ
923 for(core::map<v3s16, MapBlock*>::Iterator
924 i = modified_blocks.getIterator();
925 i.atEnd() == false; i++)
927 MapBlock *block = i.getNode()->getValue();
928 block->expireDayNightDiff();
934 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
935 core::map<v3s16, MapBlock*> &modified_blocks)
937 INodeDefManager *ndef = m_gamedef->ndef();
940 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
941 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
944 From this node to nodes underneath:
945 If lighting is sunlight (1.0), unlight neighbours and
950 v3s16 toppos = p + v3s16(0,1,0);
951 v3s16 bottompos = p + v3s16(0,-1,0);
953 bool node_under_sunlight = true;
954 core::map<v3s16, bool> light_sources;
957 Collect old node for rollback
959 RollbackNode rollback_oldnode(this, p, m_gamedef);
962 If there is a node at top and it doesn't have sunlight,
963 there has not been any sunlight going down.
965 Otherwise there probably is.
968 MapNode topnode = getNode(toppos);
970 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
971 node_under_sunlight = false;
973 catch(InvalidPositionException &e)
978 Remove all light that has come out of this node
981 enum LightBank banks[] =
986 for(s32 i=0; i<2; i++)
988 enum LightBank bank = banks[i];
990 u8 lightwas = getNode(p).getLight(bank, ndef);
992 // Add the block of the added node to modified_blocks
993 v3s16 blockpos = getNodeBlockPos(p);
994 MapBlock * block = getBlockNoCreate(blockpos);
995 assert(block != NULL);
996 modified_blocks.insert(blockpos, block);
998 assert(isValidPosition(p));
1000 // Unlight neighbours of node.
1001 // This means setting light of all consequent dimmer nodes
1003 // This also collects the nodes at the border which will spread
1004 // light again into this.
1005 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1007 n.setLight(bank, 0, ndef);
1011 If node lets sunlight through and is under sunlight, it has
1014 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1016 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1020 Remove node metadata
1023 removeNodeMetadata(p);
1026 Set the node on the map
1032 If node is under sunlight and doesn't let sunlight through,
1033 take all sunlighted nodes under it and clear light from them
1034 and from where the light has been spread.
1035 TODO: This could be optimized by mass-unlighting instead
1038 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1042 //m_dout<<DTIME<<"y="<<y<<std::endl;
1043 v3s16 n2pos(p.X, y, p.Z);
1047 n2 = getNode(n2pos);
1049 catch(InvalidPositionException &e)
1054 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1056 unLightNeighbors(LIGHTBANK_DAY,
1057 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1058 light_sources, modified_blocks);
1059 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1067 for(s32 i=0; i<2; i++)
1069 enum LightBank bank = banks[i];
1072 Spread light from all nodes that might be capable of doing so
1074 spreadLight(bank, light_sources, modified_blocks);
1078 Update information about whether day and night light differ
1080 for(core::map<v3s16, MapBlock*>::Iterator
1081 i = modified_blocks.getIterator();
1082 i.atEnd() == false; i++)
1084 MapBlock *block = i.getNode()->getValue();
1085 block->expireDayNightDiff();
1091 if(m_gamedef->rollback())
1093 RollbackNode rollback_newnode(this, p, m_gamedef);
1094 RollbackAction action;
1095 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1096 m_gamedef->rollback()->reportAction(action);
1100 Add neighboring liquid nodes and the node itself if it is
1101 liquid (=water node was added) to transform queue.
1104 v3s16(0,0,0), // self
1105 v3s16(0,0,1), // back
1106 v3s16(0,1,0), // top
1107 v3s16(1,0,0), // right
1108 v3s16(0,0,-1), // front
1109 v3s16(0,-1,0), // bottom
1110 v3s16(-1,0,0), // left
1112 for(u16 i=0; i<7; i++)
1117 v3s16 p2 = p + dirs[i];
1119 MapNode n2 = getNode(p2);
1120 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1122 m_transforming_liquid.push_back(p2);
1125 }catch(InvalidPositionException &e)
1133 void Map::removeNodeAndUpdate(v3s16 p,
1134 core::map<v3s16, MapBlock*> &modified_blocks)
1136 INodeDefManager *ndef = m_gamedef->ndef();
1138 /*PrintInfo(m_dout);
1139 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1140 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1142 bool node_under_sunlight = true;
1144 v3s16 toppos = p + v3s16(0,1,0);
1146 // Node will be replaced with this
1147 content_t replace_material = CONTENT_AIR;
1150 Collect old node for rollback
1152 RollbackNode rollback_oldnode(this, p, m_gamedef);
1155 If there is a node at top and it doesn't have sunlight,
1156 there will be no sunlight going down.
1159 MapNode topnode = getNode(toppos);
1161 if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1162 node_under_sunlight = false;
1164 catch(InvalidPositionException &e)
1168 core::map<v3s16, bool> light_sources;
1170 enum LightBank banks[] =
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1180 Unlight neighbors (in case the node is a light source)
1182 unLightNeighbors(bank, p,
1183 getNode(p).getLight(bank, ndef),
1184 light_sources, modified_blocks);
1188 Remove node metadata
1191 removeNodeMetadata(p);
1195 This also clears the lighting.
1199 n.setContent(replace_material);
1202 for(s32 i=0; i<2; i++)
1204 enum LightBank bank = banks[i];
1207 Recalculate lighting
1209 spreadLight(bank, light_sources, modified_blocks);
1212 // Add the block of the removed node to modified_blocks
1213 v3s16 blockpos = getNodeBlockPos(p);
1214 MapBlock * block = getBlockNoCreate(blockpos);
1215 assert(block != NULL);
1216 modified_blocks.insert(blockpos, block);
1219 If the removed node was under sunlight, propagate the
1220 sunlight down from it and then light all neighbors
1221 of the propagated blocks.
1223 if(node_under_sunlight)
1225 s16 ybottom = propagateSunlight(p, modified_blocks);
1226 /*m_dout<<DTIME<<"Node was under sunlight. "
1227 "Propagating sunlight";
1228 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1230 for(; y >= ybottom; y--)
1232 v3s16 p2(p.X, y, p.Z);
1233 /*m_dout<<DTIME<<"lighting neighbors of node ("
1234 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1236 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1241 // Set the lighting of this node to 0
1242 // TODO: Is this needed? Lighting is cleared up there already.
1244 MapNode n = getNode(p);
1245 n.setLight(LIGHTBANK_DAY, 0, ndef);
1248 catch(InvalidPositionException &e)
1254 for(s32 i=0; i<2; i++)
1256 enum LightBank bank = banks[i];
1258 // Get the brightest neighbour node and propagate light from it
1259 v3s16 n2p = getBrightestNeighbour(bank, p);
1261 MapNode n2 = getNode(n2p);
1262 lightNeighbors(bank, n2p, modified_blocks);
1264 catch(InvalidPositionException &e)
1270 Update information about whether day and night light differ
1272 for(core::map<v3s16, MapBlock*>::Iterator
1273 i = modified_blocks.getIterator();
1274 i.atEnd() == false; i++)
1276 MapBlock *block = i.getNode()->getValue();
1277 block->expireDayNightDiff();
1283 if(m_gamedef->rollback())
1285 RollbackNode rollback_newnode(this, p, m_gamedef);
1286 RollbackAction action;
1287 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1288 m_gamedef->rollback()->reportAction(action);
1292 Add neighboring liquid nodes and this node to transform queue.
1293 (it's vital for the node itself to get updated last.)
1296 v3s16(0,0,1), // back
1297 v3s16(0,1,0), // top
1298 v3s16(1,0,0), // right
1299 v3s16(0,0,-1), // front
1300 v3s16(0,-1,0), // bottom
1301 v3s16(-1,0,0), // left
1302 v3s16(0,0,0), // self
1304 for(u16 i=0; i<7; i++)
1309 v3s16 p2 = p + dirs[i];
1311 MapNode n2 = getNode(p2);
1312 if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1314 m_transforming_liquid.push_back(p2);
1317 }catch(InvalidPositionException &e)
1323 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1326 event.type = MEET_ADDNODE;
1330 bool succeeded = true;
1332 core::map<v3s16, MapBlock*> modified_blocks;
1333 addNodeAndUpdate(p, n, modified_blocks);
1335 // Copy modified_blocks to event
1336 for(core::map<v3s16, MapBlock*>::Iterator
1337 i = modified_blocks.getIterator();
1338 i.atEnd()==false; i++)
1340 event.modified_blocks.insert(i.getNode()->getKey(), false);
1343 catch(InvalidPositionException &e){
1347 dispatchEvent(&event);
1352 bool Map::removeNodeWithEvent(v3s16 p)
1355 event.type = MEET_REMOVENODE;
1358 bool succeeded = true;
1360 core::map<v3s16, MapBlock*> modified_blocks;
1361 removeNodeAndUpdate(p, modified_blocks);
1363 // Copy modified_blocks to event
1364 for(core::map<v3s16, MapBlock*>::Iterator
1365 i = modified_blocks.getIterator();
1366 i.atEnd()==false; i++)
1368 event.modified_blocks.insert(i.getNode()->getKey(), false);
1371 catch(InvalidPositionException &e){
1375 dispatchEvent(&event);
1380 bool Map::getDayNightDiff(v3s16 blockpos)
1383 v3s16 p = blockpos + v3s16(0,0,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->getDayNightDiff())
1388 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(-1,0,0);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->getDayNightDiff())
1396 catch(InvalidPositionException &e){}
1398 v3s16 p = blockpos + v3s16(0,-1,0);
1399 MapBlock *b = getBlockNoCreate(p);
1400 if(b->getDayNightDiff())
1403 catch(InvalidPositionException &e){}
1405 v3s16 p = blockpos + v3s16(0,0,-1);
1406 MapBlock *b = getBlockNoCreate(p);
1407 if(b->getDayNightDiff())
1410 catch(InvalidPositionException &e){}
1413 v3s16 p = blockpos + v3s16(1,0,0);
1414 MapBlock *b = getBlockNoCreate(p);
1415 if(b->getDayNightDiff())
1418 catch(InvalidPositionException &e){}
1420 v3s16 p = blockpos + v3s16(0,1,0);
1421 MapBlock *b = getBlockNoCreate(p);
1422 if(b->getDayNightDiff())
1425 catch(InvalidPositionException &e){}
1427 v3s16 p = blockpos + v3s16(0,0,1);
1428 MapBlock *b = getBlockNoCreate(p);
1429 if(b->getDayNightDiff())
1432 catch(InvalidPositionException &e){}
1438 Updates usage timers
1440 void Map::timerUpdate(float dtime, float unload_timeout,
1441 core::list<v3s16> *unloaded_blocks)
1443 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1445 // Profile modified reasons
1446 Profiler modprofiler;
1448 core::list<v2s16> sector_deletion_queue;
1449 u32 deleted_blocks_count = 0;
1450 u32 saved_blocks_count = 0;
1451 u32 block_count_all = 0;
1453 core::map<v2s16, MapSector*>::Iterator si;
1456 si = m_sectors.getIterator();
1457 for(; si.atEnd() == false; si++)
1459 MapSector *sector = si.getNode()->getValue();
1461 bool all_blocks_deleted = true;
1463 core::list<MapBlock*> blocks;
1464 sector->getBlocks(blocks);
1466 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1467 i != blocks.end(); i++)
1469 MapBlock *block = (*i);
1471 block->incrementUsageTimer(dtime);
1473 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1475 v3s16 p = block->getPos();
1478 if(block->getModified() != MOD_STATE_CLEAN
1479 && save_before_unloading)
1481 modprofiler.add(block->getModifiedReason(), 1);
1483 saved_blocks_count++;
1486 // Delete from memory
1487 sector->deleteBlock(block);
1490 unloaded_blocks->push_back(p);
1492 deleted_blocks_count++;
1496 all_blocks_deleted = false;
1501 if(all_blocks_deleted)
1503 sector_deletion_queue.push_back(si.getNode()->getKey());
1508 // Finally delete the empty sectors
1509 deleteSectors(sector_deletion_queue);
1511 if(deleted_blocks_count != 0)
1513 PrintInfo(infostream); // ServerMap/ClientMap:
1514 infostream<<"Unloaded "<<deleted_blocks_count
1515 <<" blocks from memory";
1516 if(save_before_unloading)
1517 infostream<<", of which "<<saved_blocks_count<<" were written";
1518 infostream<<", "<<block_count_all<<" blocks in memory";
1519 infostream<<"."<<std::endl;
1520 if(saved_blocks_count != 0){
1521 PrintInfo(infostream); // ServerMap/ClientMap:
1522 infostream<<"Blocks modified by: "<<std::endl;
1523 modprofiler.print(infostream);
1528 void Map::deleteSectors(core::list<v2s16> &list)
1530 core::list<v2s16>::Iterator j;
1531 for(j=list.begin(); j!=list.end(); j++)
1533 MapSector *sector = m_sectors[*j];
1534 // If sector is in sector cache, remove it from there
1535 if(m_sector_cache == sector)
1536 m_sector_cache = NULL;
1537 // Remove from map and delete
1538 m_sectors.remove(*j);
1544 void Map::unloadUnusedData(float timeout,
1545 core::list<v3s16> *deleted_blocks)
1547 core::list<v2s16> sector_deletion_queue;
1548 u32 deleted_blocks_count = 0;
1549 u32 saved_blocks_count = 0;
1551 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1552 for(; si.atEnd() == false; si++)
1554 MapSector *sector = si.getNode()->getValue();
1556 bool all_blocks_deleted = true;
1558 core::list<MapBlock*> blocks;
1559 sector->getBlocks(blocks);
1560 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1561 i != blocks.end(); i++)
1563 MapBlock *block = (*i);
1565 if(block->getUsageTimer() > timeout)
1568 if(block->getModified() != MOD_STATE_CLEAN)
1571 saved_blocks_count++;
1573 // Delete from memory
1574 sector->deleteBlock(block);
1575 deleted_blocks_count++;
1579 all_blocks_deleted = false;
1583 if(all_blocks_deleted)
1585 sector_deletion_queue.push_back(si.getNode()->getKey());
1589 deleteSectors(sector_deletion_queue);
1591 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1592 <<", of which "<<saved_blocks_count<<" were wr."
1595 //return sector_deletion_queue.getSize();
1596 //return deleted_blocks_count;
1600 void Map::PrintInfo(std::ostream &out)
1605 #define WATER_DROP_BOOST 4
1609 NEIGHBOR_SAME_LEVEL,
1612 struct NodeNeighbor {
1618 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1620 INodeDefManager *nodemgr = m_gamedef->ndef();
1622 DSTACK(__FUNCTION_NAME);
1623 //TimeTaker timer("transformLiquids()");
1626 u32 initial_size = m_transforming_liquid.size();
1628 /*if(initial_size != 0)
1629 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1631 // list of nodes that due to viscosity have not reached their max level height
1632 UniqueQueue<v3s16> must_reflow;
1634 // List of MapBlocks that will require a lighting update (due to lava)
1635 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1637 while(m_transforming_liquid.size() != 0)
1639 // This should be done here so that it is done when continue is used
1640 if(loopcount >= initial_size || loopcount >= 10000)
1645 Get a queued transforming liquid node
1647 v3s16 p0 = m_transforming_liquid.pop_front();
1649 MapNode n0 = getNodeNoEx(p0);
1652 Collect information about current node
1654 s8 liquid_level = -1;
1655 content_t liquid_kind = CONTENT_IGNORE;
1656 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1657 switch (liquid_type) {
1659 liquid_level = LIQUID_LEVEL_SOURCE;
1660 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1662 case LIQUID_FLOWING:
1663 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1664 liquid_kind = n0.getContent();
1667 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1668 // continue with the next node.
1669 if (n0.getContent() != CONTENT_AIR)
1671 liquid_kind = CONTENT_AIR;
1676 Collect information about the environment
1678 const v3s16 *dirs = g_6dirs;
1679 NodeNeighbor sources[6]; // surrounding sources
1680 int num_sources = 0;
1681 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1683 NodeNeighbor airs[6]; // surrounding air
1685 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1686 int num_neutrals = 0;
1687 bool flowing_down = false;
1688 for (u16 i = 0; i < 6; i++) {
1689 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1692 nt = NEIGHBOR_UPPER;
1695 nt = NEIGHBOR_LOWER;
1698 v3s16 npos = p0 + dirs[i];
1699 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1700 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1702 if (nb.n.getContent() == CONTENT_AIR) {
1703 airs[num_airs++] = nb;
1704 // if the current node is a water source the neighbor
1705 // should be enqueded for transformation regardless of whether the
1706 // current node changes or not.
1707 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1708 m_transforming_liquid.push_back(npos);
1709 // if the current node happens to be a flowing node, it will start to flow down here.
1710 if (nb.t == NEIGHBOR_LOWER) {
1711 flowing_down = true;
1714 neutrals[num_neutrals++] = nb;
1718 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1719 if (liquid_kind == CONTENT_AIR)
1720 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1721 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1722 neutrals[num_neutrals++] = nb;
1724 // Do not count bottom source, it will screw things up
1726 sources[num_sources++] = nb;
1729 case LIQUID_FLOWING:
1730 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1731 if (liquid_kind == CONTENT_AIR)
1732 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1733 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1734 neutrals[num_neutrals++] = nb;
1736 flows[num_flows++] = nb;
1737 if (nb.t == NEIGHBOR_LOWER)
1738 flowing_down = true;
1745 decide on the type (and possibly level) of the current node
1747 content_t new_node_content;
1748 s8 new_node_level = -1;
1749 s8 max_node_level = -1;
1750 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1751 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1752 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1753 // it's perfectly safe to use liquid_kind here to determine the new node content.
1754 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1755 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1756 // liquid_kind is set properly, see above
1757 new_node_content = liquid_kind;
1758 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1760 // no surrounding sources, so get the maximum level that can flow into this node
1761 for (u16 i = 0; i < num_flows; i++) {
1762 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1763 switch (flows[i].t) {
1764 case NEIGHBOR_UPPER:
1765 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1766 max_node_level = LIQUID_LEVEL_MAX;
1767 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1768 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1769 } else if (nb_liquid_level > max_node_level)
1770 max_node_level = nb_liquid_level;
1772 case NEIGHBOR_LOWER:
1774 case NEIGHBOR_SAME_LEVEL:
1775 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1776 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1777 max_node_level = nb_liquid_level - 1;
1783 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1784 if (viscosity > 1 && max_node_level != liquid_level) {
1785 // amount to gain, limited by viscosity
1786 // must be at least 1 in absolute value
1787 s8 level_inc = max_node_level - liquid_level;
1788 if (level_inc < -viscosity || level_inc > viscosity)
1789 new_node_level = liquid_level + level_inc/viscosity;
1790 else if (level_inc < 0)
1791 new_node_level = liquid_level - 1;
1792 else if (level_inc > 0)
1793 new_node_level = liquid_level + 1;
1794 if (new_node_level != max_node_level)
1795 must_reflow.push_back(p0);
1797 new_node_level = max_node_level;
1799 if (new_node_level >= 0)
1800 new_node_content = liquid_kind;
1802 new_node_content = CONTENT_AIR;
1807 check if anything has changed. if not, just continue with the next node.
1809 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1810 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1811 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1817 update the current node
1819 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1820 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1821 // set level to last 3 bits, flowing down bit to 4th bit
1822 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1824 // set the liquid level and flow bit to 0
1825 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1827 n0.setContent(new_node_content);
1829 // Find out whether there is a suspect for this action
1830 std::string suspect;
1831 if(m_gamedef->rollback()){
1832 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1835 if(!suspect.empty()){
1837 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1838 // Get old node for rollback
1839 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1843 RollbackNode rollback_newnode(this, p0, m_gamedef);
1844 RollbackAction action;
1845 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1846 m_gamedef->rollback()->reportAction(action);
1852 v3s16 blockpos = getNodeBlockPos(p0);
1853 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1855 modified_blocks.insert(blockpos, block);
1856 // If node emits light, MapBlock requires lighting update
1857 if(nodemgr->get(n0).light_source != 0)
1858 lighting_modified_blocks[block->getPos()] = block;
1862 enqueue neighbors for update if neccessary
1864 switch (nodemgr->get(n0.getContent()).liquid_type) {
1866 case LIQUID_FLOWING:
1867 // make sure source flows into all neighboring nodes
1868 for (u16 i = 0; i < num_flows; i++)
1869 if (flows[i].t != NEIGHBOR_UPPER)
1870 m_transforming_liquid.push_back(flows[i].p);
1871 for (u16 i = 0; i < num_airs; i++)
1872 if (airs[i].t != NEIGHBOR_UPPER)
1873 m_transforming_liquid.push_back(airs[i].p);
1876 // this flow has turned to air; neighboring flows might need to do the same
1877 for (u16 i = 0; i < num_flows; i++)
1878 m_transforming_liquid.push_back(flows[i].p);
1882 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1883 while (must_reflow.size() > 0)
1884 m_transforming_liquid.push_back(must_reflow.pop_front());
1885 updateLighting(lighting_modified_blocks, modified_blocks);
1888 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1890 v3s16 blockpos = getNodeBlockPos(p);
1891 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1892 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1894 infostream<<"Map::getNodeMetadata(): Need to emerge "
1895 <<PP(blockpos)<<std::endl;
1896 block = emergeBlock(blockpos, false);
1900 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1904 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1908 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1910 v3s16 blockpos = getNodeBlockPos(p);
1911 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1912 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1914 infostream<<"Map::setNodeMetadata(): Need to emerge "
1915 <<PP(blockpos)<<std::endl;
1916 block = emergeBlock(blockpos, false);
1920 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1924 block->m_node_metadata.set(p_rel, meta);
1927 void Map::removeNodeMetadata(v3s16 p)
1929 v3s16 blockpos = getNodeBlockPos(p);
1930 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1931 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1934 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1938 block->m_node_metadata.remove(p_rel);
1941 NodeTimer Map::getNodeTimer(v3s16 p)
1943 v3s16 blockpos = getNodeBlockPos(p);
1944 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1945 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1947 infostream<<"Map::getNodeTimer(): Need to emerge "
1948 <<PP(blockpos)<<std::endl;
1949 block = emergeBlock(blockpos, false);
1953 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1957 NodeTimer t = block->m_node_timers.get(p_rel);
1961 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1963 v3s16 blockpos = getNodeBlockPos(p);
1964 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1965 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1967 infostream<<"Map::setNodeTimer(): Need to emerge "
1968 <<PP(blockpos)<<std::endl;
1969 block = emergeBlock(blockpos, false);
1973 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1977 block->m_node_timers.set(p_rel, t);
1980 void Map::removeNodeTimer(v3s16 p)
1982 v3s16 blockpos = getNodeBlockPos(p);
1983 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1984 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1987 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1991 block->m_node_timers.remove(p_rel);
1997 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1998 Map(dout_server, gamedef),
2000 m_map_metadata_changed(true),
2002 m_database_read(NULL),
2003 m_database_write(NULL)
2005 verbosestream<<__FUNCTION_NAME<<std::endl;
2008 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2010 m_mgparams = new MapgenV6Params();
2012 m_seed = m_mgparams->seed;
2014 if (g_settings->get("fixed_map_seed").empty())
2016 m_seed = (((u64)(myrand()%0xffff)<<0)
2017 + ((u64)(myrand()%0xffff)<<16)
2018 + ((u64)(myrand()%0xffff)<<32)
2019 + ((u64)(myrand()&0xffff)<<48));
2020 m_mgparams->seed = m_seed;
2024 Experimental and debug stuff
2031 Try to load map; if not found, create a new one.
2034 m_savedir = savedir;
2035 m_map_saving_enabled = false;
2039 // If directory exists, check contents and load if possible
2040 if(fs::PathExists(m_savedir))
2042 // If directory is empty, it is safe to save into it.
2043 if(fs::GetDirListing(m_savedir).size() == 0)
2045 infostream<<"ServerMap: Empty save directory is valid."
2047 m_map_saving_enabled = true;
2052 // Load map metadata (seed, chunksize)
2055 catch(SettingNotFoundException &e){
2056 infostream<<"ServerMap: Some metadata not found."
2057 <<" Using default settings."<<std::endl;
2059 catch(FileNotGoodException &e){
2060 infostream<<"WARNING: Could not load map metadata"
2061 //<<" Disabling chunk-based generator."
2066 infostream<<"ServerMap: Successfully loaded map "
2067 <<"metadata from "<<savedir
2068 <<", assuming valid save directory."
2069 <<" seed="<<m_seed<<"."
2072 m_map_saving_enabled = true;
2073 // Map loaded, not creating new one
2077 // If directory doesn't exist, it is safe to save to it
2079 m_map_saving_enabled = true;
2082 catch(std::exception &e)
2084 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2085 <<", exception: "<<e.what()<<std::endl;
2086 infostream<<"Please remove the map or fix it."<<std::endl;
2087 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2090 infostream<<"Initializing new map."<<std::endl;
2092 // Create zero sector
2093 emergeSector(v2s16(0,0));
2095 // Initially write whole map
2096 save(MOD_STATE_CLEAN);
2099 ServerMap::~ServerMap()
2101 verbosestream<<__FUNCTION_NAME<<std::endl;
2105 if(m_map_saving_enabled)
2107 // Save only changed parts
2108 save(MOD_STATE_WRITE_AT_UNLOAD);
2109 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2113 infostream<<"ServerMap: Map not saved"<<std::endl;
2116 catch(std::exception &e)
2118 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2119 <<", exception: "<<e.what()<<std::endl;
2123 Close database if it was opened
2126 sqlite3_finalize(m_database_read);
2127 if(m_database_write)
2128 sqlite3_finalize(m_database_write);
2130 sqlite3_close(m_database);
2136 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2137 for(; i.atEnd() == false; i++)
2139 MapChunk *chunk = i.getNode()->getValue();
2145 void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2147 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2148 if(enable_mapgen_debug_info)
2149 infostream<<"initBlockMake(): "
2150 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2151 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2154 //s16 chunksize = 3;
2155 //v3s16 chunk_offset(-1,-1,-1);
2156 //s16 chunksize = 4;
2157 //v3s16 chunk_offset(-1,-1,-1);
2159 v3s16 chunk_offset(-2,-2,-2);
2160 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2161 v3s16 blockpos_min = blockpos_div * chunksize;
2162 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2163 blockpos_min += chunk_offset;
2164 blockpos_max += chunk_offset;
2166 //v3s16 extra_borders(1,1,1);
2167 v3s16 extra_borders(1,1,1);
2169 // Do nothing if not inside limits (+-1 because of neighbors)
2170 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2171 blockpos_over_limit(blockpos_max + extra_borders))
2177 data->no_op = false;
2178 data->seed = m_seed;
2179 data->blockpos_min = blockpos_min;
2180 data->blockpos_max = blockpos_max;
2181 data->blockpos_requested = blockpos;
2182 data->nodedef = m_gamedef->ndef();
2185 Create the whole area of this and the neighboring blocks
2188 //TimeTaker timer("initBlockMake() create area");
2190 for(s16 x=blockpos_min.X-extra_borders.X;
2191 x<=blockpos_max.X+extra_borders.X; x++)
2192 for(s16 z=blockpos_min.Z-extra_borders.Z;
2193 z<=blockpos_max.Z+extra_borders.Z; z++)
2195 v2s16 sectorpos(x, z);
2196 // Sector metadata is loaded from disk if not already loaded.
2197 ServerMapSector *sector = createSector(sectorpos);
2200 for(s16 y=blockpos_min.Y-extra_borders.Y;
2201 y<=blockpos_max.Y+extra_borders.Y; y++)
2204 //MapBlock *block = createBlock(p);
2205 // 1) get from memory, 2) load from disk
2206 MapBlock *block = emergeBlock(p, false);
2207 // 3) create a blank one
2210 block = createBlock(p);
2213 Block gets sunlight if this is true.
2215 Refer to the map generator heuristics.
2217 bool ug = m_emerge->isBlockUnderground(p);
2218 block->setIsUnderground(ug);
2221 // Lighting will not be valid after make_chunk is called
2222 block->setLightingExpired(true);
2223 // Lighting will be calculated
2224 //block->setLightingExpired(false);
2230 Now we have a big empty area.
2232 Make a ManualMapVoxelManipulator that contains this and the
2236 // The area that contains this block and it's neighbors
2237 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2238 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2240 data->vmanip = new ManualMapVoxelManipulator(this);
2241 //data->vmanip->setMap(this);
2245 //TimeTaker timer("initBlockMake() initialEmerge");
2246 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2249 // Data is ready now.
2252 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2253 core::map<v3s16, MapBlock*> &changed_blocks)
2255 v3s16 blockpos_min = data->blockpos_min;
2256 v3s16 blockpos_max = data->blockpos_max;
2257 v3s16 blockpos_requested = data->blockpos_requested;
2258 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2259 <<blockpos_requested.Y<<","
2260 <<blockpos_requested.Z<<")"<<std::endl;*/
2262 v3s16 extra_borders(1,1,1);
2266 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2270 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2272 /*infostream<<"Resulting vmanip:"<<std::endl;
2273 data->vmanip.print(infostream);*/
2275 // Make sure affected blocks are loaded
2276 for(s16 x=blockpos_min.X-extra_borders.X;
2277 x<=blockpos_max.X+extra_borders.X; x++)
2278 for(s16 z=blockpos_min.Z-extra_borders.Z;
2279 z<=blockpos_max.Z+extra_borders.Z; z++)
2280 for(s16 y=blockpos_min.Y-extra_borders.Y;
2281 y<=blockpos_max.Y+extra_borders.Y; y++)
2284 // Load from disk if not already in memory
2285 emergeBlock(p, false);
2289 Blit generated stuff to map
2290 NOTE: blitBackAll adds nearly everything to changed_blocks
2294 //TimeTaker timer("finishBlockMake() blitBackAll");
2295 data->vmanip->blitBackAll(&changed_blocks);
2298 if(enable_mapgen_debug_info)
2299 infostream<<"finishBlockMake: changed_blocks.size()="
2300 <<changed_blocks.size()<<std::endl;
2303 Copy transforming liquid information
2305 while(data->transforming_liquid.size() > 0)
2307 v3s16 p = data->transforming_liquid.pop_front();
2308 m_transforming_liquid.push_back(p);
2312 Do stuff in central blocks
2320 TimeTaker t("finishBlockMake lighting update");
2322 core::map<v3s16, MapBlock*> lighting_update_blocks;
2325 for(s16 x=blockpos_min.X-extra_borders.X;
2326 x<=blockpos_max.X+extra_borders.X; x++)
2327 for(s16 z=blockpos_min.Z-extra_borders.Z;
2328 z<=blockpos_max.Z+extra_borders.Z; z++)
2329 for(s16 y=blockpos_min.Y-extra_borders.Y;
2330 y<=blockpos_max.Y+extra_borders.Y; y++)
2333 MapBlock *block = getBlockNoCreateNoEx(p);
2335 lighting_update_blocks.insert(block->getPos(), block);
2338 updateLighting(lighting_update_blocks, changed_blocks);
2342 Set lighting to non-expired state in all of them.
2343 This is cheating, but it is not fast enough if all of them
2344 would actually be updated.
2346 for(s16 x=blockpos_min.X-extra_borders.X;
2347 x<=blockpos_max.X+extra_borders.X; x++)
2348 for(s16 z=blockpos_min.Z-extra_borders.Z;
2349 z<=blockpos_max.Z+extra_borders.Z; z++)
2350 for(s16 y=blockpos_min.Y-extra_borders.Y;
2351 y<=blockpos_max.Y+extra_borders.Y; y++)
2354 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2358 if(enable_mapgen_debug_info == false)
2359 t.stop(true); // Hide output
2364 Go through changed blocks
2366 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2367 i.atEnd() == false; i++)
2369 MapBlock *block = i.getNode()->getValue();
2372 Update day/night difference cache of the MapBlocks
2374 block->expireDayNightDiff();
2376 Set block as modified
2378 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2379 "finishBlockMake expireDayNightDiff");
2383 Set central blocks as generated
2385 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2386 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2387 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2390 MapBlock *block = getBlockNoCreateNoEx(p);
2392 block->setGenerated(true);
2396 Save changed parts of map
2397 NOTE: Will be saved later.
2399 //save(MOD_STATE_WRITE_AT_UNLOAD);
2401 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2402 <<","<<blockpos_requested.Y<<","
2403 <<blockpos_requested.Z<<")"<<std::endl;*/
2405 if(enable_mapgen_debug_info)
2408 Analyze resulting blocks
2410 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2411 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2412 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2413 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2414 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2415 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2417 v3s16 p = v3s16(x,y,z);
2418 MapBlock *block = getBlockNoCreateNoEx(p);
2420 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2421 infostream<<"Generated "<<spos<<": "
2422 <<analyze_block(block)<<std::endl;
2427 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2433 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2435 DSTACKF("%s: p2d=(%d,%d)",
2440 Check if it exists already in memory
2442 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2447 Try to load it from disk (with blocks)
2449 //if(loadSectorFull(p2d) == true)
2452 Try to load metadata from disk
2455 if(loadSectorMeta(p2d) == true)
2457 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2460 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2461 throw InvalidPositionException("");
2467 Do not create over-limit
2469 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2470 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2471 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2472 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2473 throw InvalidPositionException("createSector(): pos. over limit");
2476 Generate blank sector
2479 sector = new ServerMapSector(this, p2d, m_gamedef);
2481 // Sector position on map in nodes
2482 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2487 m_sectors.insert(p2d, sector);
2494 This is a quick-hand function for calling makeBlock().
2496 MapBlock * ServerMap::generateBlock(
2498 core::map<v3s16, MapBlock*> &modified_blocks
2501 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2503 /*infostream<<"generateBlock(): "
2504 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2507 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2509 TimeTaker timer("generateBlock");
2511 //MapBlock *block = original_dummy;
2513 v2s16 p2d(p.X, p.Z);
2514 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2517 Do not generate over-limit
2519 if(blockpos_over_limit(p))
2521 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2522 throw InvalidPositionException("generateBlock(): pos. over limit");
2526 Create block make data
2529 initBlockMake(&data, p);
2535 TimeTaker t("mapgen::make_block()");
2536 mapgen->makeChunk(&data);
2537 //mapgen::make_block(&data);
2539 if(enable_mapgen_debug_info == false)
2540 t.stop(true); // Hide output
2544 Blit data back on map, update lighting, add mobs and whatever this does
2546 finishBlockMake(&data, modified_blocks);
2551 MapBlock *block = getBlockNoCreateNoEx(p);
2559 bool erroneus_content = false;
2560 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2561 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2562 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2565 MapNode n = block->getNode(p);
2566 if(n.getContent() == CONTENT_IGNORE)
2568 infostream<<"CONTENT_IGNORE at "
2569 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2571 erroneus_content = true;
2575 if(erroneus_content)
2584 Generate a completely empty block
2588 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2589 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2591 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2594 n.setContent(CONTENT_AIR);
2595 block->setNode(v3s16(x0,y0,z0), n);
2601 if(enable_mapgen_debug_info == false)
2602 timer.stop(true); // Hide output
2608 MapBlock * ServerMap::createBlock(v3s16 p)
2610 DSTACKF("%s: p=(%d,%d,%d)",
2611 __FUNCTION_NAME, p.X, p.Y, p.Z);
2614 Do not create over-limit
2616 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2617 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2618 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2619 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2620 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2621 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2622 throw InvalidPositionException("createBlock(): pos. over limit");
2624 v2s16 p2d(p.X, p.Z);
2627 This will create or load a sector if not found in memory.
2628 If block exists on disk, it will be loaded.
2630 NOTE: On old save formats, this will be slow, as it generates
2631 lighting on blocks for them.
2633 ServerMapSector *sector;
2635 sector = (ServerMapSector*)createSector(p2d);
2636 assert(sector->getId() == MAPSECTOR_SERVER);
2638 catch(InvalidPositionException &e)
2640 infostream<<"createBlock: createSector() failed"<<std::endl;
2644 NOTE: This should not be done, or at least the exception
2645 should not be passed on as std::exception, because it
2646 won't be catched at all.
2648 /*catch(std::exception &e)
2650 infostream<<"createBlock: createSector() failed: "
2651 <<e.what()<<std::endl;
2656 Try to get a block from the sector
2659 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2662 if(block->isDummy())
2667 block = sector->createBlankBlock(block_y);
2672 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2674 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2676 p.X, p.Y, p.Z, create_blank);
2679 MapBlock *block = getBlockNoCreateNoEx(p);
2680 if(block && block->isDummy() == false)
2685 MapBlock *block = loadBlock(p);
2691 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2692 MapBlock *block = sector->createBlankBlock(p.Y);
2696 /*if(allow_generate)
2698 core::map<v3s16, MapBlock*> modified_blocks;
2699 MapBlock *block = generateBlock(p, modified_blocks);
2703 event.type = MEET_OTHER;
2706 // Copy modified_blocks to event
2707 for(core::map<v3s16, MapBlock*>::Iterator
2708 i = modified_blocks.getIterator();
2709 i.atEnd()==false; i++)
2711 event.modified_blocks.insert(i.getNode()->getKey(), false);
2715 dispatchEvent(&event);
2724 s16 ServerMap::findGroundLevel(v2s16 p2d)
2728 Uh, just do something random...
2730 // Find existing map from top to down
2733 v3s16 p(p2d.X, max, p2d.Y);
2734 for(; p.Y>min; p.Y--)
2736 MapNode n = getNodeNoEx(p);
2737 if(n.getContent() != CONTENT_IGNORE)
2742 // If this node is not air, go to plan b
2743 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2745 // Search existing walkable and return it
2746 for(; p.Y>min; p.Y--)
2748 MapNode n = getNodeNoEx(p);
2749 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2758 Determine from map generator noise functions
2761 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2764 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2765 //return (s16)level;
2768 void ServerMap::createDatabase() {
2771 e = sqlite3_exec(m_database,
2772 "CREATE TABLE IF NOT EXISTS `blocks` ("
2773 "`pos` INT NOT NULL PRIMARY KEY,"
2776 , NULL, NULL, NULL);
2777 if(e == SQLITE_ABORT)
2778 throw FileNotGoodException("Could not create database structure");
2780 infostream<<"ServerMap: Database structure was created";
2783 void ServerMap::verifyDatabase() {
2788 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2789 bool needs_create = false;
2793 Open the database connection
2796 createDirs(m_savedir);
2798 if(!fs::PathExists(dbp))
2799 needs_create = true;
2801 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2802 if(d != SQLITE_OK) {
2803 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2804 throw FileNotGoodException("Cannot open database file");
2810 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2811 if(d != SQLITE_OK) {
2812 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2813 throw FileNotGoodException("Cannot prepare read statement");
2816 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2817 if(d != SQLITE_OK) {
2818 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2819 throw FileNotGoodException("Cannot prepare write statement");
2822 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2823 if(d != SQLITE_OK) {
2824 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2825 throw FileNotGoodException("Cannot prepare read statement");
2828 infostream<<"ServerMap: Database opened"<<std::endl;
2832 bool ServerMap::loadFromFolders() {
2833 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2838 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2839 return (sqlite3_int64)pos.Z*16777216 +
2840 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2843 void ServerMap::createDirs(std::string path)
2845 if(fs::CreateAllDirs(path) == false)
2847 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2848 <<"\""<<path<<"\""<<std::endl;
2849 throw BaseException("ServerMap failed to create directory");
2853 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2859 snprintf(cc, 9, "%.4x%.4x",
2860 (unsigned int)pos.X&0xffff,
2861 (unsigned int)pos.Y&0xffff);
2863 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2865 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2866 (unsigned int)pos.X&0xfff,
2867 (unsigned int)pos.Y&0xfff);
2869 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2875 v2s16 ServerMap::getSectorPos(std::string dirname)
2879 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2880 assert(spos != std::string::npos);
2881 if(dirname.size() - spos == 8)
2884 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2886 else if(dirname.size() - spos == 3)
2889 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2890 // Sign-extend the 12 bit values up to 16 bits...
2891 if(x&0x800) x|=0xF000;
2892 if(y&0x800) y|=0xF000;
2899 v2s16 pos((s16)x, (s16)y);
2903 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2905 v2s16 p2d = getSectorPos(sectordir);
2907 if(blockfile.size() != 4){
2908 throw InvalidFilenameException("Invalid block filename");
2911 int r = sscanf(blockfile.c_str(), "%4x", &y);
2913 throw InvalidFilenameException("Invalid block filename");
2914 return v3s16(p2d.X, y, p2d.Y);
2917 std::string ServerMap::getBlockFilename(v3s16 p)
2920 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2924 void ServerMap::save(ModifiedState save_level)
2926 DSTACK(__FUNCTION_NAME);
2927 if(m_map_saving_enabled == false)
2929 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2933 if(save_level == MOD_STATE_CLEAN)
2934 infostream<<"ServerMap: Saving whole map, this can take time."
2937 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2942 // Profile modified reasons
2943 Profiler modprofiler;
2945 u32 sector_meta_count = 0;
2946 u32 block_count = 0;
2947 u32 block_count_all = 0; // Number of blocks in memory
2949 // Don't do anything with sqlite unless something is really saved
2950 bool save_started = false;
2952 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2953 for(; i.atEnd() == false; i++)
2955 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2956 assert(sector->getId() == MAPSECTOR_SERVER);
2958 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2960 saveSectorMeta(sector);
2961 sector_meta_count++;
2963 core::list<MapBlock*> blocks;
2964 sector->getBlocks(blocks);
2965 core::list<MapBlock*>::Iterator j;
2967 for(j=blocks.begin(); j!=blocks.end(); j++)
2969 MapBlock *block = *j;
2973 if(block->getModified() >= save_level)
2978 save_started = true;
2981 modprofiler.add(block->getModifiedReason(), 1);
2986 /*infostream<<"ServerMap: Written block ("
2987 <<block->getPos().X<<","
2988 <<block->getPos().Y<<","
2989 <<block->getPos().Z<<")"
2998 Only print if something happened or saved whole map
3000 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3001 || block_count != 0)
3003 infostream<<"ServerMap: Written: "
3004 <<sector_meta_count<<" sector metadata files, "
3005 <<block_count<<" block files"
3006 <<", "<<block_count_all<<" blocks in memory."
3008 PrintInfo(infostream); // ServerMap/ClientMap:
3009 infostream<<"Blocks modified by: "<<std::endl;
3010 modprofiler.print(infostream);
3014 static s32 unsignedToSigned(s32 i, s32 max_positive)
3016 if(i < max_positive)
3019 return i - 2*max_positive;
3022 // modulo of a negative number does not work consistently in C
3023 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3027 return mod - ((-i) % mod);
3030 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3032 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3034 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3036 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3037 return v3s16(x,y,z);
3040 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
3042 if(loadFromFolders()){
3043 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3044 <<"all blocks that are stored in flat files"<<std::endl;
3050 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3052 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3053 v3s16 p = getIntegerAsBlock(block_i);
3054 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3060 void ServerMap::saveMapMeta()
3062 DSTACK(__FUNCTION_NAME);
3064 /*infostream<<"ServerMap::saveMapMeta(): "
3068 createDirs(m_savedir);
3070 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3071 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3072 if(os.good() == false)
3074 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3075 <<"could not open"<<fullpath<<std::endl;
3076 throw FileNotGoodException("Cannot open chunk metadata");
3081 params.set("mg_name", m_emerge->params->mg_name);
3082 params.setU64("seed", m_emerge->params->seed);
3083 params.setS16("water_level", m_emerge->params->water_level);
3084 params.setS16("chunksize", m_emerge->params->chunksize);
3085 params.setS32("mg_flags", m_emerge->params->flags);
3087 m_emerge->params->writeParams(¶ms);
3089 params.writeLines(os);
3091 os<<"[end_of_params]\n";
3093 m_map_metadata_changed = false;
3096 void ServerMap::loadMapMeta()
3098 DSTACK(__FUNCTION_NAME);
3100 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3103 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3104 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3105 if(is.good() == false)
3107 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3108 <<"could not open"<<fullpath<<std::endl;
3109 throw FileNotGoodException("Cannot open map metadata");
3117 throw SerializationError
3118 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3120 std::getline(is, line);
3121 std::string trimmedline = trim(line);
3122 if(trimmedline == "[end_of_params]")
3124 params.parseConfigLine(line);
3127 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3131 m_mgparams = mgparams;
3132 m_seed = mgparams->seed;
3134 if (params.exists("seed")) {
3135 m_seed = params.getU64("seed");
3136 m_mgparams->seed = m_seed;
3140 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3143 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3145 DSTACK(__FUNCTION_NAME);
3146 // Format used for writing
3147 u8 version = SER_FMT_VER_HIGHEST;
3149 v2s16 pos = sector->getPos();
3150 std::string dir = getSectorDir(pos);
3153 std::string fullpath = dir + DIR_DELIM + "meta";
3154 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3155 if(o.good() == false)
3156 throw FileNotGoodException("Cannot open sector metafile");
3158 sector->serialize(o, version);
3160 sector->differs_from_disk = false;
3163 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3165 DSTACK(__FUNCTION_NAME);
3167 v2s16 p2d = getSectorPos(sectordir);
3169 ServerMapSector *sector = NULL;
3171 std::string fullpath = sectordir + DIR_DELIM + "meta";
3172 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3173 if(is.good() == false)
3175 // If the directory exists anyway, it probably is in some old
3176 // format. Just go ahead and create the sector.
3177 if(fs::PathExists(sectordir))
3179 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3180 <<fullpath<<" doesn't exist but directory does."
3181 <<" Continuing with a sector with no metadata."
3183 sector = new ServerMapSector(this, p2d, m_gamedef);
3184 m_sectors.insert(p2d, sector);
3188 throw FileNotGoodException("Cannot open sector metafile");
3193 sector = ServerMapSector::deSerialize
3194 (is, this, p2d, m_sectors, m_gamedef);
3196 saveSectorMeta(sector);
3199 sector->differs_from_disk = false;
3204 bool ServerMap::loadSectorMeta(v2s16 p2d)
3206 DSTACK(__FUNCTION_NAME);
3208 MapSector *sector = NULL;
3210 // The directory layout we're going to load from.
3211 // 1 - original sectors/xxxxzzzz/
3212 // 2 - new sectors2/xxx/zzz/
3213 // If we load from anything but the latest structure, we will
3214 // immediately save to the new one, and remove the old.
3216 std::string sectordir1 = getSectorDir(p2d, 1);
3217 std::string sectordir;
3218 if(fs::PathExists(sectordir1))
3220 sectordir = sectordir1;
3225 sectordir = getSectorDir(p2d, 2);
3229 sector = loadSectorMeta(sectordir, loadlayout != 2);
3231 catch(InvalidFilenameException &e)
3235 catch(FileNotGoodException &e)
3239 catch(std::exception &e)
3248 bool ServerMap::loadSectorFull(v2s16 p2d)
3250 DSTACK(__FUNCTION_NAME);
3252 MapSector *sector = NULL;
3254 // The directory layout we're going to load from.
3255 // 1 - original sectors/xxxxzzzz/
3256 // 2 - new sectors2/xxx/zzz/
3257 // If we load from anything but the latest structure, we will
3258 // immediately save to the new one, and remove the old.
3260 std::string sectordir1 = getSectorDir(p2d, 1);
3261 std::string sectordir;
3262 if(fs::PathExists(sectordir1))
3264 sectordir = sectordir1;
3269 sectordir = getSectorDir(p2d, 2);
3273 sector = loadSectorMeta(sectordir, loadlayout != 2);
3275 catch(InvalidFilenameException &e)
3279 catch(FileNotGoodException &e)
3283 catch(std::exception &e)
3291 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3293 std::vector<fs::DirListNode>::iterator i2;
3294 for(i2=list2.begin(); i2!=list2.end(); i2++)
3300 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3302 catch(InvalidFilenameException &e)
3304 // This catches unknown crap in directory
3310 infostream<<"Sector converted to new layout - deleting "<<
3311 sectordir1<<std::endl;
3312 fs::RecursiveDelete(sectordir1);
3319 void ServerMap::beginSave() {
3321 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3322 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3325 void ServerMap::endSave() {
3327 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3328 infostream<<"WARNING: endSave() failed, map might not have saved.";
3331 void ServerMap::saveBlock(MapBlock *block)
3333 DSTACK(__FUNCTION_NAME);
3335 Dummy blocks are not written
3337 if(block->isDummy())
3339 /*v3s16 p = block->getPos();
3340 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3341 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3345 // Format used for writing
3346 u8 version = SER_FMT_VER_HIGHEST;
3348 v3s16 p3d = block->getPos();
3352 v2s16 p2d(p3d.X, p3d.Z);
3353 std::string sectordir = getSectorDir(p2d);
3355 createDirs(sectordir);
3357 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3358 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3359 if(o.good() == false)
3360 throw FileNotGoodException("Cannot open block data");
3363 [0] u8 serialization version
3369 std::ostringstream o(std::ios_base::binary);
3371 o.write((char*)&version, 1);
3374 block->serialize(o, version, true);
3376 // Write block to database
3378 std::string tmp = o.str();
3379 const char *bytes = tmp.c_str();
3381 bool success = true;
3382 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3383 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3386 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3387 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3390 int written = sqlite3_step(m_database_write);
3391 if(written != SQLITE_DONE) {
3392 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3393 <<sqlite3_errmsg(m_database)<<std::endl;
3396 // Make ready for later reuse
3397 sqlite3_reset(m_database_write);
3399 // We just wrote it to the disk so clear modified flag
3401 block->resetModified();
3404 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3406 DSTACK(__FUNCTION_NAME);
3408 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3411 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3412 if(is.good() == false)
3413 throw FileNotGoodException("Cannot open block file");
3415 v3s16 p3d = getBlockPos(sectordir, blockfile);
3416 v2s16 p2d(p3d.X, p3d.Z);
3418 assert(sector->getPos() == p2d);
3420 u8 version = SER_FMT_VER_INVALID;
3421 is.read((char*)&version, 1);
3424 throw SerializationError("ServerMap::loadBlock(): Failed"
3425 " to read MapBlock version");
3427 /*u32 block_size = MapBlock::serializedLength(version);
3428 SharedBuffer<u8> data(block_size);
3429 is.read((char*)*data, block_size);*/
3431 // This will always return a sector because we're the server
3432 //MapSector *sector = emergeSector(p2d);
3434 MapBlock *block = NULL;
3435 bool created_new = false;
3436 block = sector->getBlockNoCreateNoEx(p3d.Y);
3439 block = sector->createBlankBlockNoInsert(p3d.Y);
3444 block->deSerialize(is, version, true);
3446 // If it's a new block, insert it to the map
3448 sector->insertBlock(block);
3451 Save blocks loaded in old format in new format
3454 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3458 // Should be in database now, so delete the old file
3459 fs::RecursiveDelete(fullpath);
3462 // We just loaded it from the disk, so it's up-to-date.
3463 block->resetModified();
3466 catch(SerializationError &e)
3468 infostream<<"WARNING: Invalid block data on disk "
3469 <<"fullpath="<<fullpath
3470 <<" (SerializationError). "
3471 <<"what()="<<e.what()
3473 //" Ignoring. A new one will be generated.
3476 // TODO: Backup file; name is in fullpath.
3480 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3482 DSTACK(__FUNCTION_NAME);
3485 std::istringstream is(*blob, std::ios_base::binary);
3487 u8 version = SER_FMT_VER_INVALID;
3488 is.read((char*)&version, 1);
3491 throw SerializationError("ServerMap::loadBlock(): Failed"
3492 " to read MapBlock version");
3494 /*u32 block_size = MapBlock::serializedLength(version);
3495 SharedBuffer<u8> data(block_size);
3496 is.read((char*)*data, block_size);*/
3498 // This will always return a sector because we're the server
3499 //MapSector *sector = emergeSector(p2d);
3501 MapBlock *block = NULL;
3502 bool created_new = false;
3503 block = sector->getBlockNoCreateNoEx(p3d.Y);
3506 block = sector->createBlankBlockNoInsert(p3d.Y);
3511 block->deSerialize(is, version, true);
3513 // If it's a new block, insert it to the map
3515 sector->insertBlock(block);
3518 Save blocks loaded in old format in new format
3521 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3522 // Only save if asked to; no need to update version
3526 // We just loaded it from, so it's up-to-date.
3527 block->resetModified();
3530 catch(SerializationError &e)
3532 errorstream<<"Invalid block data in database"
3533 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3534 <<" (SerializationError): "<<e.what()<<std::endl;
3536 // TODO: Block should be marked as invalid in memory so that it is
3537 // not touched but the game can run
3539 if(g_settings->getBool("ignore_world_load_errors")){
3540 errorstream<<"Ignoring block load error. Duck and cover! "
3541 <<"(ignore_world_load_errors)"<<std::endl;
3543 throw SerializationError("Invalid block data in database");
3549 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3551 DSTACK(__FUNCTION_NAME);
3553 v2s16 p2d(blockpos.X, blockpos.Z);
3555 if(!loadFromFolders()) {
3558 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3559 infostream<<"WARNING: Could not bind block position for load: "
3560 <<sqlite3_errmsg(m_database)<<std::endl;
3561 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3563 Make sure sector is loaded
3565 MapSector *sector = createSector(p2d);
3570 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3571 size_t len = sqlite3_column_bytes(m_database_read, 0);
3573 std::string datastr(data, len);
3575 loadBlock(&datastr, blockpos, sector, false);
3577 sqlite3_step(m_database_read);
3578 // We should never get more than 1 row, so ok to reset
3579 sqlite3_reset(m_database_read);
3581 return getBlockNoCreateNoEx(blockpos);
3583 sqlite3_reset(m_database_read);
3585 // Not found in database, try the files
3588 // The directory layout we're going to load from.
3589 // 1 - original sectors/xxxxzzzz/
3590 // 2 - new sectors2/xxx/zzz/
3591 // If we load from anything but the latest structure, we will
3592 // immediately save to the new one, and remove the old.
3594 std::string sectordir1 = getSectorDir(p2d, 1);
3595 std::string sectordir;
3596 if(fs::PathExists(sectordir1))
3598 sectordir = sectordir1;
3603 sectordir = getSectorDir(p2d, 2);
3607 Make sure sector is loaded
3609 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3613 sector = loadSectorMeta(sectordir, loadlayout != 2);
3615 catch(InvalidFilenameException &e)
3619 catch(FileNotGoodException &e)
3623 catch(std::exception &e)
3630 Make sure file exists
3633 std::string blockfilename = getBlockFilename(blockpos);
3634 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3638 Load block and save it to the database
3640 loadBlock(sectordir, blockfilename, sector, true);
3641 return getBlockNoCreateNoEx(blockpos);
3644 void ServerMap::PrintInfo(std::ostream &out)
3653 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3658 MapVoxelManipulator::~MapVoxelManipulator()
3660 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3664 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3666 TimeTaker timer1("emerge", &emerge_time);
3668 // Units of these are MapBlocks
3669 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3670 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3672 VoxelArea block_area_nodes
3673 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3675 addArea(block_area_nodes);
3677 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3678 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3679 for(s32 x=p_min.X; x<=p_max.X; x++)
3682 core::map<v3s16, bool>::Node *n;
3683 n = m_loaded_blocks.find(p);
3687 bool block_data_inexistent = false;
3690 TimeTaker timer1("emerge load", &emerge_load_time);
3692 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3693 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3695 a.print(infostream);
3696 infostream<<std::endl;*/
3698 MapBlock *block = m_map->getBlockNoCreate(p);
3699 if(block->isDummy())
3700 block_data_inexistent = true;
3702 block->copyTo(*this);
3704 catch(InvalidPositionException &e)
3706 block_data_inexistent = true;
3709 if(block_data_inexistent)
3711 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3712 // Fill with VOXELFLAG_INEXISTENT
3713 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3714 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3716 s32 i = m_area.index(a.MinEdge.X,y,z);
3717 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3721 m_loaded_blocks.insert(p, !block_data_inexistent);
3724 //infostream<<"emerge done"<<std::endl;
3728 SUGG: Add an option to only update eg. water and air nodes.
3729 This will make it interfere less with important stuff if
3732 void MapVoxelManipulator::blitBack
3733 (core::map<v3s16, MapBlock*> & modified_blocks)
3735 if(m_area.getExtent() == v3s16(0,0,0))
3738 //TimeTaker timer1("blitBack");
3740 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3741 <<m_loaded_blocks.size()<<std::endl;*/
3744 Initialize block cache
3746 v3s16 blockpos_last;
3747 MapBlock *block = NULL;
3748 bool block_checked_in_modified = false;
3750 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3751 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3752 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3756 u8 f = m_flags[m_area.index(p)];
3757 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3760 MapNode &n = m_data[m_area.index(p)];
3762 v3s16 blockpos = getNodeBlockPos(p);
3767 if(block == NULL || blockpos != blockpos_last){
3768 block = m_map->getBlockNoCreate(blockpos);
3769 blockpos_last = blockpos;
3770 block_checked_in_modified = false;
3773 // Calculate relative position in block
3774 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3776 // Don't continue if nothing has changed here
3777 if(block->getNode(relpos) == n)
3780 //m_map->setNode(m_area.MinEdge + p, n);
3781 block->setNode(relpos, n);
3784 Make sure block is in modified_blocks
3786 if(block_checked_in_modified == false)
3788 modified_blocks[blockpos] = block;
3789 block_checked_in_modified = true;
3792 catch(InvalidPositionException &e)
3798 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3799 MapVoxelManipulator(map),
3800 m_create_area(false)
3804 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3808 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3810 // Just create the area so that it can be pointed to
3811 VoxelManipulator::emerge(a, caller_id);
3814 void ManualMapVoxelManipulator::initialEmerge(
3815 v3s16 blockpos_min, v3s16 blockpos_max)
3817 TimeTaker timer1("initialEmerge", &emerge_time);
3819 // Units of these are MapBlocks
3820 v3s16 p_min = blockpos_min;
3821 v3s16 p_max = blockpos_max;
3823 VoxelArea block_area_nodes
3824 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3826 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3829 infostream<<"initialEmerge: area: ";
3830 block_area_nodes.print(infostream);
3831 infostream<<" ("<<size_MB<<"MB)";
3832 infostream<<std::endl;
3835 addArea(block_area_nodes);
3837 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3838 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3839 for(s32 x=p_min.X; x<=p_max.X; x++)
3842 core::map<v3s16, bool>::Node *n;
3843 n = m_loaded_blocks.find(p);
3847 bool block_data_inexistent = false;
3850 TimeTaker timer1("emerge load", &emerge_load_time);
3852 MapBlock *block = m_map->getBlockNoCreate(p);
3853 if(block->isDummy())
3854 block_data_inexistent = true;
3856 block->copyTo(*this);
3858 catch(InvalidPositionException &e)
3860 block_data_inexistent = true;
3863 if(block_data_inexistent)
3866 Mark area inexistent
3868 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3869 // Fill with VOXELFLAG_INEXISTENT
3870 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3871 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3873 s32 i = m_area.index(a.MinEdge.X,y,z);
3874 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3878 m_loaded_blocks.insert(p, !block_data_inexistent);
3882 void ManualMapVoxelManipulator::blitBackAll(
3883 core::map<v3s16, MapBlock*> * modified_blocks)
3885 if(m_area.getExtent() == v3s16(0,0,0))
3889 Copy data of all blocks
3891 for(core::map<v3s16, bool>::Iterator
3892 i = m_loaded_blocks.getIterator();
3893 i.atEnd() == false; i++)
3895 v3s16 p = i.getNode()->getKey();
3896 bool existed = i.getNode()->getValue();
3897 if(existed == false)
3899 // The Great Bug was found using this
3900 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3901 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3905 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3908 infostream<<"WARNING: "<<__FUNCTION_NAME
3909 <<": got NULL block "
3910 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3915 block->copyFrom(*this);
3918 modified_blocks->insert(p, block);