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 {
1616 bool l; //can liquid
1620 void Map::transforming_liquid_add(v3s16 p) {
1621 m_transforming_liquid.push_back(p);
1624 s32 Map::transforming_liquid_size() {
1625 return m_transforming_liquid.size();
1628 const v3s16 g_7dirs[7] =
1630 // +right, +top, +back
1631 v3s16( 0,-1, 0), // bottom
1632 v3s16( 0, 0, 0), // self
1633 v3s16( 0, 0, 1), // back
1634 v3s16( 0, 0,-1), // front
1635 v3s16( 1, 0, 0), // right
1636 v3s16(-1, 0, 0), // left
1637 v3s16( 0, 1, 0) // top
1644 void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks)
1646 INodeDefManager *nodemgr = m_gamedef->ndef();
1648 DSTACK(__FUNCTION_NAME);
1649 //TimeTaker timer("transformLiquids()");
1652 u32 initial_size = m_transforming_liquid.size();
1654 u8 relax = g_settings->getS16("liquid_relax");
1655 bool fast_flood = g_settings->getS16("liquid_fast_flood");
1656 int water_level = g_settings->getS16("water_level");
1658 /*if(initial_size != 0)
1659 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1661 // list of nodes that due to viscosity have not reached their max level height
1662 UniqueQueue<v3s16> must_reflow, must_reflow_second;
1664 // List of MapBlocks that will require a lighting update (due to lava)
1665 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1667 while(m_transforming_liquid.size() > 0)
1669 // This should be done here so that it is done when continue is used
1670 if(loopcount >= initial_size || loopcount >= 1000)
1674 Get a queued transforming liquid node
1676 v3s16 p0 = m_transforming_liquid.pop_front();
1677 u16 total_level = 0;
1678 NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes
1679 s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block
1680 s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels
1681 s8 can_liquid_same_level = 0;
1682 content_t liquid_kind = CONTENT_IGNORE;
1683 content_t liquid_kind_flowing = CONTENT_IGNORE;
1685 Collect information about the environment
1687 const v3s16 *dirs = g_7dirs;
1688 for (u16 i = 0; i < 7; i++) {
1689 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1692 nt = NEIGHBOR_UPPER;
1695 nt = NEIGHBOR_LOWER;
1698 v3s16 npos = p0 + dirs[i];
1700 neighbors[i].n = getNodeNoEx(npos);
1701 neighbors[i].t = nt;
1702 neighbors[i].p = npos;
1705 NodeNeighbor & nb = neighbors[i];
1707 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1709 if (nb.n.getContent() == CONTENT_AIR) {
1710 liquid_levels[i] = 0;
1715 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1716 if (liquid_kind_flowing == CONTENT_IGNORE)
1717 liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1718 if (liquid_kind == CONTENT_IGNORE)
1719 liquid_kind = nb.n.getContent();
1720 if (nb.n.getContent() == liquid_kind) {
1721 liquid_levels[i] = LIQUID_LEVEL_SOURCE;
1723 nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK);
1726 case LIQUID_FLOWING:
1727 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1728 if (liquid_kind_flowing == CONTENT_IGNORE)
1729 liquid_kind_flowing = nb.n.getContent();
1730 if (liquid_kind == CONTENT_IGNORE)
1731 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source);
1732 if (nb.n.getContent() == liquid_kind_flowing) {
1733 liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK);
1738 if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level;
1739 if (liquid_levels[i] > 0) total_level += liquid_levels[i];
1742 infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type
1743 //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
1744 << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl;
1748 if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0)
1751 // fill bottom block
1752 if (neighbors[D_BOTTOM].l) {
1753 liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level;
1754 total_level -= liquid_levels_want[D_BOTTOM];
1757 if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up
1758 total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level;
1761 // calculate self level 5 blocks
1763 total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level
1764 ? LIQUID_LEVEL_SOURCE
1765 : total_level / can_liquid_same_level;
1766 total_level -= want_level * can_liquid_same_level;
1768 if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down
1772 for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level
1773 if (!neighbors[ii].l)
1775 liquid_levels_want[ii] = want_level;
1776 if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0
1777 && liquid_levels[ii] > liquid_levels_want[ii]
1779 ++liquid_levels_want[ii];
1784 for (u16 ii = 0; ii < 7; ++ii) {
1785 if (total_level < 1) break;
1786 if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) {
1787 ++liquid_levels_want[ii];
1792 // fill top block if can
1793 if (neighbors[D_TOP].l) {
1794 liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
1795 total_level -= liquid_levels_want[D_TOP];
1798 for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization
1799 if (liquid_levels_want[ii] >= 0 &&
1801 (fast_flood && p0.Y < water_level &&
1802 (initial_size >= 1000
1804 && want_level >= LIQUID_LEVEL_SOURCE/4
1805 && can_liquid_same_level >= 5
1806 && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE))))
1807 liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE;
1809 //if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
1812 for (u16 i = 0; i < 7; i++) {
1813 if (liquid_levels_want[i] < 0 || !neighbors[i].l)
1815 MapNode & n0 = neighbors[i].n;
1816 p0 = neighbors[i].p;
1818 decide on the type (and possibly level) of the current node
1820 content_t new_node_content;
1821 s8 new_node_level = -1;
1822 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1823 if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) {
1824 // amount to gain, limited by viscosity
1825 // must be at least 1 in absolute value
1826 s8 level_inc = liquid_levels_want[i] - liquid_levels[i];
1827 if (level_inc < -viscosity || level_inc > viscosity)
1828 new_node_level = liquid_levels[i] + level_inc/viscosity;
1829 else if (level_inc < 0)
1830 new_node_level = liquid_levels[i] - 1;
1831 else if (level_inc > 0)
1832 new_node_level = liquid_levels[i] + 1;
1834 new_node_level = liquid_levels_want[i];
1835 if (new_node_level >= LIQUID_LEVEL_SOURCE)
1836 new_node_content = liquid_kind;
1837 else if (new_node_level > 0)
1838 new_node_content = liquid_kind_flowing;
1840 new_node_content = CONTENT_AIR;
1842 // last level must flow down on stairs
1843 if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 //
1844 for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1845 if (!neighbors[ii].l)
1847 must_reflow_second.push_back(p0 + dirs[ii]);
1851 check if anything has changed. if not, just continue with the next node.
1854 new_node_content == n0.getContent()
1855 && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1856 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level
1857 // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down
1860 (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE ||
1861 (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i
1869 update the current node
1871 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1872 // set level to last 3 bits, flowing down bit to 4th bit
1873 n0.param2 = (new_node_level & LIQUID_LEVEL_MASK);
1874 } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) {
1875 //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1876 n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00);
1878 //infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl;
1879 n0.setContent(new_node_content);
1880 // Find out whether there is a suspect for this action
1881 std::string suspect;
1882 if(m_gamedef->rollback()){
1883 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1886 if(!suspect.empty()){
1888 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1889 // Get old node for rollback
1890 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1894 RollbackNode rollback_newnode(this, p0, m_gamedef);
1895 RollbackAction action;
1896 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1897 m_gamedef->rollback()->reportAction(action);
1903 v3s16 blockpos = getNodeBlockPos(p0);
1904 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1906 modified_blocks.insert(blockpos, block);
1907 // If node emits light, MapBlock requires lighting update
1908 if(nodemgr->get(n0).light_source != 0)
1909 lighting_modified_blocks[block->getPos()] = block;
1911 must_reflow.push_back(neighbors[i].p);
1913 /* //for better relax
1914 if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level
1915 if (!neighbors[ii].l) continue;
1916 must_reflow.push_back(p0 + dirs[ii]);
1919 //if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl;
1920 while (must_reflow.size() > 0)
1921 m_transforming_liquid.push_back(must_reflow.pop_front());
1922 while (must_reflow_second.size() > 0)
1923 m_transforming_liquid.push_back(must_reflow_second.pop_front());
1924 updateLighting(lighting_modified_blocks, modified_blocks);
1927 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1930 if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks);
1932 INodeDefManager *nodemgr = m_gamedef->ndef();
1934 DSTACK(__FUNCTION_NAME);
1935 //TimeTaker timer("transformLiquids()");
1938 u32 initial_size = m_transforming_liquid.size();
1940 /*if(initial_size != 0)
1941 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1943 // list of nodes that due to viscosity have not reached their max level height
1944 UniqueQueue<v3s16> must_reflow;
1946 // List of MapBlocks that will require a lighting update (due to lava)
1947 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1949 while(m_transforming_liquid.size() != 0)
1951 // This should be done here so that it is done when continue is used
1952 if(loopcount >= initial_size || loopcount >= 10000)
1957 Get a queued transforming liquid node
1959 v3s16 p0 = m_transforming_liquid.pop_front();
1961 MapNode n0 = getNodeNoEx(p0);
1964 Collect information about current node
1966 s8 liquid_level = -1;
1967 content_t liquid_kind = CONTENT_IGNORE;
1968 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1969 switch (liquid_type) {
1971 liquid_level = LIQUID_LEVEL_SOURCE;
1972 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1974 case LIQUID_FLOWING:
1975 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1976 liquid_kind = n0.getContent();
1979 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1980 // continue with the next node.
1981 if (n0.getContent() != CONTENT_AIR)
1983 liquid_kind = CONTENT_AIR;
1988 Collect information about the environment
1990 const v3s16 *dirs = g_6dirs;
1991 NodeNeighbor sources[6]; // surrounding sources
1992 int num_sources = 0;
1993 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1995 NodeNeighbor airs[6]; // surrounding air
1997 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1998 int num_neutrals = 0;
1999 bool flowing_down = false;
2000 for (u16 i = 0; i < 6; i++) {
2001 NeighborType nt = NEIGHBOR_SAME_LEVEL;
2004 nt = NEIGHBOR_UPPER;
2007 nt = NEIGHBOR_LOWER;
2010 v3s16 npos = p0 + dirs[i];
2011 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
2012 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
2014 if (nb.n.getContent() == CONTENT_AIR) {
2015 airs[num_airs++] = nb;
2016 // if the current node is a water source the neighbor
2017 // should be enqueded for transformation regardless of whether the
2018 // current node changes or not.
2019 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
2020 m_transforming_liquid.push_back(npos);
2021 // if the current node happens to be a flowing node, it will start to flow down here.
2022 if (nb.t == NEIGHBOR_LOWER) {
2023 flowing_down = true;
2026 neutrals[num_neutrals++] = nb;
2030 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2031 if (liquid_kind == CONTENT_AIR)
2032 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2033 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2034 neutrals[num_neutrals++] = nb;
2036 // Do not count bottom source, it will screw things up
2038 sources[num_sources++] = nb;
2041 case LIQUID_FLOWING:
2042 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2043 if (liquid_kind == CONTENT_AIR)
2044 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
2045 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
2046 neutrals[num_neutrals++] = nb;
2048 flows[num_flows++] = nb;
2049 if (nb.t == NEIGHBOR_LOWER)
2050 flowing_down = true;
2057 decide on the type (and possibly level) of the current node
2059 content_t new_node_content;
2060 s8 new_node_level = -1;
2061 s8 max_node_level = -1;
2062 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
2063 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
2064 // or the flowing alternative of the first of the surrounding sources (if it's air), so
2065 // it's perfectly safe to use liquid_kind here to determine the new node content.
2066 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
2067 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
2068 // liquid_kind is set properly, see above
2069 new_node_content = liquid_kind;
2070 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
2072 // no surrounding sources, so get the maximum level that can flow into this node
2073 for (u16 i = 0; i < num_flows; i++) {
2074 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
2075 switch (flows[i].t) {
2076 case NEIGHBOR_UPPER:
2077 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
2078 max_node_level = LIQUID_LEVEL_MAX;
2079 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
2080 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
2081 } else if (nb_liquid_level > max_node_level)
2082 max_node_level = nb_liquid_level;
2084 case NEIGHBOR_LOWER:
2086 case NEIGHBOR_SAME_LEVEL:
2087 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
2088 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
2089 max_node_level = nb_liquid_level - 1;
2095 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
2096 if (viscosity > 1 && max_node_level != liquid_level) {
2097 // amount to gain, limited by viscosity
2098 // must be at least 1 in absolute value
2099 s8 level_inc = max_node_level - liquid_level;
2100 if (level_inc < -viscosity || level_inc > viscosity)
2101 new_node_level = liquid_level + level_inc/viscosity;
2102 else if (level_inc < 0)
2103 new_node_level = liquid_level - 1;
2104 else if (level_inc > 0)
2105 new_node_level = liquid_level + 1;
2106 if (new_node_level != max_node_level)
2107 must_reflow.push_back(p0);
2109 new_node_level = max_node_level;
2111 if (new_node_level >= 0)
2112 new_node_content = liquid_kind;
2114 new_node_content = CONTENT_AIR;
2119 check if anything has changed. if not, just continue with the next node.
2121 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
2122 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
2123 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
2129 update the current node
2131 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2132 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
2133 // set level to last 3 bits, flowing down bit to 4th bit
2134 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
2136 // set the liquid level and flow bit to 0
2137 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
2139 n0.setContent(new_node_content);
2141 // Find out whether there is a suspect for this action
2142 std::string suspect;
2143 if(m_gamedef->rollback()){
2144 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
2147 if(!suspect.empty()){
2149 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
2150 // Get old node for rollback
2151 RollbackNode rollback_oldnode(this, p0, m_gamedef);
2155 RollbackNode rollback_newnode(this, p0, m_gamedef);
2156 RollbackAction action;
2157 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
2158 m_gamedef->rollback()->reportAction(action);
2164 v3s16 blockpos = getNodeBlockPos(p0);
2165 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2167 modified_blocks.insert(blockpos, block);
2168 // If node emits light, MapBlock requires lighting update
2169 if(nodemgr->get(n0).light_source != 0)
2170 lighting_modified_blocks[block->getPos()] = block;
2174 enqueue neighbors for update if neccessary
2176 switch (nodemgr->get(n0.getContent()).liquid_type) {
2178 case LIQUID_FLOWING:
2179 // make sure source flows into all neighboring nodes
2180 for (u16 i = 0; i < num_flows; i++)
2181 if (flows[i].t != NEIGHBOR_UPPER)
2182 m_transforming_liquid.push_back(flows[i].p);
2183 for (u16 i = 0; i < num_airs; i++)
2184 if (airs[i].t != NEIGHBOR_UPPER)
2185 m_transforming_liquid.push_back(airs[i].p);
2188 // this flow has turned to air; neighboring flows might need to do the same
2189 for (u16 i = 0; i < num_flows; i++)
2190 m_transforming_liquid.push_back(flows[i].p);
2194 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2195 while (must_reflow.size() > 0)
2196 m_transforming_liquid.push_back(must_reflow.pop_front());
2197 updateLighting(lighting_modified_blocks, modified_blocks);
2200 NodeMetadata* Map::getNodeMetadata(v3s16 p)
2202 v3s16 blockpos = getNodeBlockPos(p);
2203 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2204 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2206 infostream<<"Map::getNodeMetadata(): Need to emerge "
2207 <<PP(blockpos)<<std::endl;
2208 block = emergeBlock(blockpos, false);
2212 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
2216 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
2220 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
2222 v3s16 blockpos = getNodeBlockPos(p);
2223 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2224 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2226 infostream<<"Map::setNodeMetadata(): Need to emerge "
2227 <<PP(blockpos)<<std::endl;
2228 block = emergeBlock(blockpos, false);
2232 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
2236 block->m_node_metadata.set(p_rel, meta);
2239 void Map::removeNodeMetadata(v3s16 p)
2241 v3s16 blockpos = getNodeBlockPos(p);
2242 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2243 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2246 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2250 block->m_node_metadata.remove(p_rel);
2253 NodeTimer Map::getNodeTimer(v3s16 p)
2255 v3s16 blockpos = getNodeBlockPos(p);
2256 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2257 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2259 infostream<<"Map::getNodeTimer(): Need to emerge "
2260 <<PP(blockpos)<<std::endl;
2261 block = emergeBlock(blockpos, false);
2265 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2269 NodeTimer t = block->m_node_timers.get(p_rel);
2273 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2275 v3s16 blockpos = getNodeBlockPos(p);
2276 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2277 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2279 infostream<<"Map::setNodeTimer(): Need to emerge "
2280 <<PP(blockpos)<<std::endl;
2281 block = emergeBlock(blockpos, false);
2285 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2289 block->m_node_timers.set(p_rel, t);
2292 void Map::removeNodeTimer(v3s16 p)
2294 v3s16 blockpos = getNodeBlockPos(p);
2295 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2296 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2299 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2303 block->m_node_timers.remove(p_rel);
2309 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2310 Map(dout_server, gamedef),
2312 m_map_metadata_changed(true),
2314 m_database_read(NULL),
2315 m_database_write(NULL)
2317 verbosestream<<__FUNCTION_NAME<<std::endl;
2320 m_mgparams = m_emerge->getParamsFromSettings(g_settings);
2322 m_mgparams = new MapgenV6Params();
2324 m_seed = m_mgparams->seed;
2326 if (g_settings->get("fixed_map_seed").empty())
2328 m_seed = (((u64)(myrand() & 0xffff) << 0)
2329 | ((u64)(myrand() & 0xffff) << 16)
2330 | ((u64)(myrand() & 0xffff) << 32)
2331 | ((u64)(myrand() & 0xffff) << 48));
2332 m_mgparams->seed = m_seed;
2336 Experimental and debug stuff
2343 Try to load map; if not found, create a new one.
2346 m_savedir = savedir;
2347 m_map_saving_enabled = false;
2351 // If directory exists, check contents and load if possible
2352 if(fs::PathExists(m_savedir))
2354 // If directory is empty, it is safe to save into it.
2355 if(fs::GetDirListing(m_savedir).size() == 0)
2357 infostream<<"ServerMap: Empty save directory is valid."
2359 m_map_saving_enabled = true;
2364 // Load map metadata (seed, chunksize)
2367 catch(SettingNotFoundException &e){
2368 infostream<<"ServerMap: Some metadata not found."
2369 <<" Using default settings."<<std::endl;
2371 catch(FileNotGoodException &e){
2372 infostream<<"WARNING: Could not load map metadata"
2373 //<<" Disabling chunk-based generator."
2378 infostream<<"ServerMap: Successfully loaded map "
2379 <<"metadata from "<<savedir
2380 <<", assuming valid save directory."
2381 <<" seed="<<m_seed<<"."
2384 m_map_saving_enabled = true;
2385 // Map loaded, not creating new one
2389 // If directory doesn't exist, it is safe to save to it
2391 m_map_saving_enabled = true;
2394 catch(std::exception &e)
2396 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2397 <<", exception: "<<e.what()<<std::endl;
2398 infostream<<"Please remove the map or fix it."<<std::endl;
2399 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2402 infostream<<"Initializing new map."<<std::endl;
2404 // Create zero sector
2405 emergeSector(v2s16(0,0));
2407 // Initially write whole map
2408 save(MOD_STATE_CLEAN);
2411 ServerMap::~ServerMap()
2413 verbosestream<<__FUNCTION_NAME<<std::endl;
2417 if(m_map_saving_enabled)
2419 // Save only changed parts
2420 save(MOD_STATE_WRITE_AT_UNLOAD);
2421 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2425 infostream<<"ServerMap: Map not saved"<<std::endl;
2428 catch(std::exception &e)
2430 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2431 <<", exception: "<<e.what()<<std::endl;
2435 Close database if it was opened
2438 sqlite3_finalize(m_database_read);
2439 if(m_database_write)
2440 sqlite3_finalize(m_database_write);
2442 sqlite3_close(m_database);
2448 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2449 for(; i.atEnd() == false; i++)
2451 MapChunk *chunk = i.getNode()->getValue();
2457 void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2459 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2460 if(enable_mapgen_debug_info)
2461 infostream<<"initBlockMake(): "
2462 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2463 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2466 //s16 chunksize = 3;
2467 //v3s16 chunk_offset(-1,-1,-1);
2468 //s16 chunksize = 4;
2469 //v3s16 chunk_offset(-1,-1,-1);
2471 v3s16 chunk_offset(-2,-2,-2);
2472 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2473 v3s16 blockpos_min = blockpos_div * chunksize;
2474 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2475 blockpos_min += chunk_offset;
2476 blockpos_max += chunk_offset;
2478 //v3s16 extra_borders(1,1,1);
2479 v3s16 extra_borders(1,1,1);
2481 // Do nothing if not inside limits (+-1 because of neighbors)
2482 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2483 blockpos_over_limit(blockpos_max + extra_borders))
2489 data->no_op = false;
2490 data->seed = m_seed;
2491 data->blockpos_min = blockpos_min;
2492 data->blockpos_max = blockpos_max;
2493 data->blockpos_requested = blockpos;
2494 data->nodedef = m_gamedef->ndef();
2497 Create the whole area of this and the neighboring blocks
2500 //TimeTaker timer("initBlockMake() create area");
2502 for(s16 x=blockpos_min.X-extra_borders.X;
2503 x<=blockpos_max.X+extra_borders.X; x++)
2504 for(s16 z=blockpos_min.Z-extra_borders.Z;
2505 z<=blockpos_max.Z+extra_borders.Z; z++)
2507 v2s16 sectorpos(x, z);
2508 // Sector metadata is loaded from disk if not already loaded.
2509 ServerMapSector *sector = createSector(sectorpos);
2512 for(s16 y=blockpos_min.Y-extra_borders.Y;
2513 y<=blockpos_max.Y+extra_borders.Y; y++)
2516 //MapBlock *block = createBlock(p);
2517 // 1) get from memory, 2) load from disk
2518 MapBlock *block = emergeBlock(p, false);
2519 // 3) create a blank one
2522 block = createBlock(p);
2525 Block gets sunlight if this is true.
2527 Refer to the map generator heuristics.
2529 bool ug = m_emerge->isBlockUnderground(p);
2530 block->setIsUnderground(ug);
2533 // Lighting will not be valid after make_chunk is called
2534 block->setLightingExpired(true);
2535 // Lighting will be calculated
2536 //block->setLightingExpired(false);
2542 Now we have a big empty area.
2544 Make a ManualMapVoxelManipulator that contains this and the
2548 // The area that contains this block and it's neighbors
2549 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2550 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2552 data->vmanip = new ManualMapVoxelManipulator(this);
2553 //data->vmanip->setMap(this);
2557 //TimeTaker timer("initBlockMake() initialEmerge");
2558 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2561 // Data is ready now.
2564 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2565 core::map<v3s16, MapBlock*> &changed_blocks)
2567 v3s16 blockpos_min = data->blockpos_min;
2568 v3s16 blockpos_max = data->blockpos_max;
2569 v3s16 blockpos_requested = data->blockpos_requested;
2570 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2571 <<blockpos_requested.Y<<","
2572 <<blockpos_requested.Z<<")"<<std::endl;*/
2574 v3s16 extra_borders(1,1,1);
2578 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2582 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2584 /*infostream<<"Resulting vmanip:"<<std::endl;
2585 data->vmanip.print(infostream);*/
2587 // Make sure affected blocks are loaded
2588 for(s16 x=blockpos_min.X-extra_borders.X;
2589 x<=blockpos_max.X+extra_borders.X; x++)
2590 for(s16 z=blockpos_min.Z-extra_borders.Z;
2591 z<=blockpos_max.Z+extra_borders.Z; z++)
2592 for(s16 y=blockpos_min.Y-extra_borders.Y;
2593 y<=blockpos_max.Y+extra_borders.Y; y++)
2596 // Load from disk if not already in memory
2597 emergeBlock(p, false);
2601 Blit generated stuff to map
2602 NOTE: blitBackAll adds nearly everything to changed_blocks
2606 //TimeTaker timer("finishBlockMake() blitBackAll");
2607 data->vmanip->blitBackAll(&changed_blocks);
2610 if(enable_mapgen_debug_info)
2611 infostream<<"finishBlockMake: changed_blocks.size()="
2612 <<changed_blocks.size()<<std::endl;
2615 Copy transforming liquid information
2617 while(data->transforming_liquid.size() > 0)
2619 v3s16 p = data->transforming_liquid.pop_front();
2620 m_transforming_liquid.push_back(p);
2624 Do stuff in central blocks
2632 TimeTaker t("finishBlockMake lighting update");
2634 core::map<v3s16, MapBlock*> lighting_update_blocks;
2637 for(s16 x=blockpos_min.X-extra_borders.X;
2638 x<=blockpos_max.X+extra_borders.X; x++)
2639 for(s16 z=blockpos_min.Z-extra_borders.Z;
2640 z<=blockpos_max.Z+extra_borders.Z; z++)
2641 for(s16 y=blockpos_min.Y-extra_borders.Y;
2642 y<=blockpos_max.Y+extra_borders.Y; y++)
2645 MapBlock *block = getBlockNoCreateNoEx(p);
2647 lighting_update_blocks.insert(block->getPos(), block);
2650 updateLighting(lighting_update_blocks, changed_blocks);
2654 Set lighting to non-expired state in all of them.
2655 This is cheating, but it is not fast enough if all of them
2656 would actually be updated.
2658 for(s16 x=blockpos_min.X-extra_borders.X;
2659 x<=blockpos_max.X+extra_borders.X; x++)
2660 for(s16 z=blockpos_min.Z-extra_borders.Z;
2661 z<=blockpos_max.Z+extra_borders.Z; z++)
2662 for(s16 y=blockpos_min.Y-extra_borders.Y;
2663 y<=blockpos_max.Y+extra_borders.Y; y++)
2666 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2670 if(enable_mapgen_debug_info == false)
2671 t.stop(true); // Hide output
2676 Go through changed blocks
2678 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2679 i.atEnd() == false; i++)
2681 MapBlock *block = i.getNode()->getValue();
2684 Update day/night difference cache of the MapBlocks
2686 block->expireDayNightDiff();
2688 Set block as modified
2690 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2691 "finishBlockMake expireDayNightDiff");
2695 Set central blocks as generated
2697 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2698 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2699 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2702 MapBlock *block = getBlockNoCreateNoEx(p);
2704 block->setGenerated(true);
2708 Save changed parts of map
2709 NOTE: Will be saved later.
2711 //save(MOD_STATE_WRITE_AT_UNLOAD);
2713 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2714 <<","<<blockpos_requested.Y<<","
2715 <<blockpos_requested.Z<<")"<<std::endl;*/
2717 if(enable_mapgen_debug_info)
2720 Analyze resulting blocks
2722 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2723 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2724 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2725 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2726 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2727 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2729 v3s16 p = v3s16(x,y,z);
2730 MapBlock *block = getBlockNoCreateNoEx(p);
2732 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2733 infostream<<"Generated "<<spos<<": "
2734 <<analyze_block(block)<<std::endl;
2739 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2745 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2747 DSTACKF("%s: p2d=(%d,%d)",
2752 Check if it exists already in memory
2754 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2759 Try to load it from disk (with blocks)
2761 //if(loadSectorFull(p2d) == true)
2764 Try to load metadata from disk
2767 if(loadSectorMeta(p2d) == true)
2769 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2772 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2773 throw InvalidPositionException("");
2779 Do not create over-limit
2781 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2782 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2783 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2784 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2785 throw InvalidPositionException("createSector(): pos. over limit");
2788 Generate blank sector
2791 sector = new ServerMapSector(this, p2d, m_gamedef);
2793 // Sector position on map in nodes
2794 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2799 m_sectors.insert(p2d, sector);
2806 This is a quick-hand function for calling makeBlock().
2808 MapBlock * ServerMap::generateBlock(
2810 core::map<v3s16, MapBlock*> &modified_blocks
2813 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2815 /*infostream<<"generateBlock(): "
2816 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2819 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2821 TimeTaker timer("generateBlock");
2823 //MapBlock *block = original_dummy;
2825 v2s16 p2d(p.X, p.Z);
2826 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2829 Do not generate over-limit
2831 if(blockpos_over_limit(p))
2833 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2834 throw InvalidPositionException("generateBlock(): pos. over limit");
2838 Create block make data
2841 initBlockMake(&data, p);
2847 TimeTaker t("mapgen::make_block()");
2848 mapgen->makeChunk(&data);
2849 //mapgen::make_block(&data);
2851 if(enable_mapgen_debug_info == false)
2852 t.stop(true); // Hide output
2856 Blit data back on map, update lighting, add mobs and whatever this does
2858 finishBlockMake(&data, modified_blocks);
2863 MapBlock *block = getBlockNoCreateNoEx(p);
2871 bool erroneus_content = false;
2872 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2873 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2874 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2877 MapNode n = block->getNode(p);
2878 if(n.getContent() == CONTENT_IGNORE)
2880 infostream<<"CONTENT_IGNORE at "
2881 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2883 erroneus_content = true;
2887 if(erroneus_content)
2896 Generate a completely empty block
2900 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2901 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2903 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2906 n.setContent(CONTENT_AIR);
2907 block->setNode(v3s16(x0,y0,z0), n);
2913 if(enable_mapgen_debug_info == false)
2914 timer.stop(true); // Hide output
2920 MapBlock * ServerMap::createBlock(v3s16 p)
2922 DSTACKF("%s: p=(%d,%d,%d)",
2923 __FUNCTION_NAME, p.X, p.Y, p.Z);
2926 Do not create over-limit
2928 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2929 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2930 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2931 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2932 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2933 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2934 throw InvalidPositionException("createBlock(): pos. over limit");
2936 v2s16 p2d(p.X, p.Z);
2939 This will create or load a sector if not found in memory.
2940 If block exists on disk, it will be loaded.
2942 NOTE: On old save formats, this will be slow, as it generates
2943 lighting on blocks for them.
2945 ServerMapSector *sector;
2947 sector = (ServerMapSector*)createSector(p2d);
2948 assert(sector->getId() == MAPSECTOR_SERVER);
2950 catch(InvalidPositionException &e)
2952 infostream<<"createBlock: createSector() failed"<<std::endl;
2956 NOTE: This should not be done, or at least the exception
2957 should not be passed on as std::exception, because it
2958 won't be catched at all.
2960 /*catch(std::exception &e)
2962 infostream<<"createBlock: createSector() failed: "
2963 <<e.what()<<std::endl;
2968 Try to get a block from the sector
2971 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2974 if(block->isDummy())
2979 block = sector->createBlankBlock(block_y);
2984 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2986 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2988 p.X, p.Y, p.Z, create_blank);
2991 MapBlock *block = getBlockNoCreateNoEx(p);
2992 if(block && block->isDummy() == false)
2997 MapBlock *block = loadBlock(p);
3003 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
3004 MapBlock *block = sector->createBlankBlock(p.Y);
3008 /*if(allow_generate)
3010 core::map<v3s16, MapBlock*> modified_blocks;
3011 MapBlock *block = generateBlock(p, modified_blocks);
3015 event.type = MEET_OTHER;
3018 // Copy modified_blocks to event
3019 for(core::map<v3s16, MapBlock*>::Iterator
3020 i = modified_blocks.getIterator();
3021 i.atEnd()==false; i++)
3023 event.modified_blocks.insert(i.getNode()->getKey(), false);
3027 dispatchEvent(&event);
3036 s16 ServerMap::findGroundLevel(v2s16 p2d)
3040 Uh, just do something random...
3042 // Find existing map from top to down
3045 v3s16 p(p2d.X, max, p2d.Y);
3046 for(; p.Y>min; p.Y--)
3048 MapNode n = getNodeNoEx(p);
3049 if(n.getContent() != CONTENT_IGNORE)
3054 // If this node is not air, go to plan b
3055 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
3057 // Search existing walkable and return it
3058 for(; p.Y>min; p.Y--)
3060 MapNode n = getNodeNoEx(p);
3061 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
3070 Determine from map generator noise functions
3073 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
3076 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
3077 //return (s16)level;
3080 void ServerMap::createDatabase() {
3083 e = sqlite3_exec(m_database,
3084 "CREATE TABLE IF NOT EXISTS `blocks` ("
3085 "`pos` INT NOT NULL PRIMARY KEY,"
3088 , NULL, NULL, NULL);
3089 if(e == SQLITE_ABORT)
3090 throw FileNotGoodException("Could not create database structure");
3092 infostream<<"ServerMap: Database structure was created";
3095 void ServerMap::verifyDatabase() {
3100 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
3101 bool needs_create = false;
3105 Open the database connection
3108 createDirs(m_savedir);
3110 if(!fs::PathExists(dbp))
3111 needs_create = true;
3113 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
3114 if(d != SQLITE_OK) {
3115 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
3116 throw FileNotGoodException("Cannot open database file");
3122 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
3123 if(d != SQLITE_OK) {
3124 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3125 throw FileNotGoodException("Cannot prepare read statement");
3128 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
3129 if(d != SQLITE_OK) {
3130 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3131 throw FileNotGoodException("Cannot prepare write statement");
3134 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
3135 if(d != SQLITE_OK) {
3136 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
3137 throw FileNotGoodException("Cannot prepare read statement");
3140 infostream<<"ServerMap: Database opened"<<std::endl;
3144 bool ServerMap::loadFromFolders() {
3145 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
3150 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
3151 return (sqlite3_int64)pos.Z*16777216 +
3152 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
3155 void ServerMap::createDirs(std::string path)
3157 if(fs::CreateAllDirs(path) == false)
3159 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3160 <<"\""<<path<<"\""<<std::endl;
3161 throw BaseException("ServerMap failed to create directory");
3165 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
3171 snprintf(cc, 9, "%.4x%.4x",
3172 (unsigned int)pos.X&0xffff,
3173 (unsigned int)pos.Y&0xffff);
3175 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
3177 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
3178 (unsigned int)pos.X&0xfff,
3179 (unsigned int)pos.Y&0xfff);
3181 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
3187 v2s16 ServerMap::getSectorPos(std::string dirname)
3191 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
3192 assert(spos != std::string::npos);
3193 if(dirname.size() - spos == 8)
3196 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
3198 else if(dirname.size() - spos == 3)
3201 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
3202 // Sign-extend the 12 bit values up to 16 bits...
3203 if(x&0x800) x|=0xF000;
3204 if(y&0x800) y|=0xF000;
3211 v2s16 pos((s16)x, (s16)y);
3215 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3217 v2s16 p2d = getSectorPos(sectordir);
3219 if(blockfile.size() != 4){
3220 throw InvalidFilenameException("Invalid block filename");
3223 int r = sscanf(blockfile.c_str(), "%4x", &y);
3225 throw InvalidFilenameException("Invalid block filename");
3226 return v3s16(p2d.X, y, p2d.Y);
3229 std::string ServerMap::getBlockFilename(v3s16 p)
3232 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
3236 void ServerMap::save(ModifiedState save_level)
3238 DSTACK(__FUNCTION_NAME);
3239 if(m_map_saving_enabled == false)
3241 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
3245 if(save_level == MOD_STATE_CLEAN)
3246 infostream<<"ServerMap: Saving whole map, this can take time."
3249 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
3254 // Profile modified reasons
3255 Profiler modprofiler;
3257 u32 sector_meta_count = 0;
3258 u32 block_count = 0;
3259 u32 block_count_all = 0; // Number of blocks in memory
3261 // Don't do anything with sqlite unless something is really saved
3262 bool save_started = false;
3264 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3265 for(; i.atEnd() == false; i++)
3267 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3268 assert(sector->getId() == MAPSECTOR_SERVER);
3270 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3272 saveSectorMeta(sector);
3273 sector_meta_count++;
3275 core::list<MapBlock*> blocks;
3276 sector->getBlocks(blocks);
3277 core::list<MapBlock*>::Iterator j;
3279 for(j=blocks.begin(); j!=blocks.end(); j++)
3281 MapBlock *block = *j;
3285 if(block->getModified() >= save_level)
3290 save_started = true;
3293 modprofiler.add(block->getModifiedReason(), 1);
3298 /*infostream<<"ServerMap: Written block ("
3299 <<block->getPos().X<<","
3300 <<block->getPos().Y<<","
3301 <<block->getPos().Z<<")"
3310 Only print if something happened or saved whole map
3312 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3313 || block_count != 0)
3315 infostream<<"ServerMap: Written: "
3316 <<sector_meta_count<<" sector metadata files, "
3317 <<block_count<<" block files"
3318 <<", "<<block_count_all<<" blocks in memory."
3320 PrintInfo(infostream); // ServerMap/ClientMap:
3321 infostream<<"Blocks modified by: "<<std::endl;
3322 modprofiler.print(infostream);
3326 static s32 unsignedToSigned(s32 i, s32 max_positive)
3328 if(i < max_positive)
3331 return i - 2*max_positive;
3334 // modulo of a negative number does not work consistently in C
3335 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
3339 return mod - ((-i) % mod);
3342 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
3344 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3346 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3348 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
3349 return v3s16(x,y,z);
3352 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
3354 if(loadFromFolders()){
3355 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3356 <<"all blocks that are stored in flat files"<<std::endl;
3362 while(sqlite3_step(m_database_list) == SQLITE_ROW)
3364 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
3365 v3s16 p = getIntegerAsBlock(block_i);
3366 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
3372 void ServerMap::saveMapMeta()
3374 DSTACK(__FUNCTION_NAME);
3376 /*infostream<<"ServerMap::saveMapMeta(): "
3380 createDirs(m_savedir);
3382 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3383 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3384 if(os.good() == false)
3386 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3387 <<"could not open"<<fullpath<<std::endl;
3388 throw FileNotGoodException("Cannot open chunk metadata");
3393 m_emerge->setParamsToSettings(¶ms);
3394 params.writeLines(os);
3396 os<<"[end_of_params]\n";
3398 m_map_metadata_changed = false;
3401 void ServerMap::loadMapMeta()
3403 DSTACK(__FUNCTION_NAME);
3405 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3408 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3409 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3410 if(is.good() == false)
3412 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3413 <<"could not open"<<fullpath<<std::endl;
3414 throw FileNotGoodException("Cannot open map metadata");
3422 throw SerializationError
3423 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3425 std::getline(is, line);
3426 std::string trimmedline = trim(line);
3427 if(trimmedline == "[end_of_params]")
3429 params.parseConfigLine(line);
3432 MapgenParams *mgparams = m_emerge->getParamsFromSettings(¶ms);
3436 m_mgparams = mgparams;
3437 m_seed = mgparams->seed;
3439 if (params.exists("seed")) {
3440 m_seed = params.getU64("seed");
3441 m_mgparams->seed = m_seed;
3445 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3448 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3450 DSTACK(__FUNCTION_NAME);
3451 // Format used for writing
3452 u8 version = SER_FMT_VER_HIGHEST;
3454 v2s16 pos = sector->getPos();
3455 std::string dir = getSectorDir(pos);
3458 std::string fullpath = dir + DIR_DELIM + "meta";
3459 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3460 if(o.good() == false)
3461 throw FileNotGoodException("Cannot open sector metafile");
3463 sector->serialize(o, version);
3465 sector->differs_from_disk = false;
3468 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3470 DSTACK(__FUNCTION_NAME);
3472 v2s16 p2d = getSectorPos(sectordir);
3474 ServerMapSector *sector = NULL;
3476 std::string fullpath = sectordir + DIR_DELIM + "meta";
3477 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3478 if(is.good() == false)
3480 // If the directory exists anyway, it probably is in some old
3481 // format. Just go ahead and create the sector.
3482 if(fs::PathExists(sectordir))
3484 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3485 <<fullpath<<" doesn't exist but directory does."
3486 <<" Continuing with a sector with no metadata."
3488 sector = new ServerMapSector(this, p2d, m_gamedef);
3489 m_sectors.insert(p2d, sector);
3493 throw FileNotGoodException("Cannot open sector metafile");
3498 sector = ServerMapSector::deSerialize
3499 (is, this, p2d, m_sectors, m_gamedef);
3501 saveSectorMeta(sector);
3504 sector->differs_from_disk = false;
3509 bool ServerMap::loadSectorMeta(v2s16 p2d)
3511 DSTACK(__FUNCTION_NAME);
3513 MapSector *sector = NULL;
3515 // The directory layout we're going to load from.
3516 // 1 - original sectors/xxxxzzzz/
3517 // 2 - new sectors2/xxx/zzz/
3518 // If we load from anything but the latest structure, we will
3519 // immediately save to the new one, and remove the old.
3521 std::string sectordir1 = getSectorDir(p2d, 1);
3522 std::string sectordir;
3523 if(fs::PathExists(sectordir1))
3525 sectordir = sectordir1;
3530 sectordir = getSectorDir(p2d, 2);
3534 sector = loadSectorMeta(sectordir, loadlayout != 2);
3536 catch(InvalidFilenameException &e)
3540 catch(FileNotGoodException &e)
3544 catch(std::exception &e)
3553 bool ServerMap::loadSectorFull(v2s16 p2d)
3555 DSTACK(__FUNCTION_NAME);
3557 MapSector *sector = NULL;
3559 // The directory layout we're going to load from.
3560 // 1 - original sectors/xxxxzzzz/
3561 // 2 - new sectors2/xxx/zzz/
3562 // If we load from anything but the latest structure, we will
3563 // immediately save to the new one, and remove the old.
3565 std::string sectordir1 = getSectorDir(p2d, 1);
3566 std::string sectordir;
3567 if(fs::PathExists(sectordir1))
3569 sectordir = sectordir1;
3574 sectordir = getSectorDir(p2d, 2);
3578 sector = loadSectorMeta(sectordir, loadlayout != 2);
3580 catch(InvalidFilenameException &e)
3584 catch(FileNotGoodException &e)
3588 catch(std::exception &e)
3596 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3598 std::vector<fs::DirListNode>::iterator i2;
3599 for(i2=list2.begin(); i2!=list2.end(); i2++)
3605 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3607 catch(InvalidFilenameException &e)
3609 // This catches unknown crap in directory
3615 infostream<<"Sector converted to new layout - deleting "<<
3616 sectordir1<<std::endl;
3617 fs::RecursiveDelete(sectordir1);
3624 void ServerMap::beginSave() {
3626 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3627 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3630 void ServerMap::endSave() {
3632 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3633 infostream<<"WARNING: endSave() failed, map might not have saved.";
3636 void ServerMap::saveBlock(MapBlock *block)
3638 DSTACK(__FUNCTION_NAME);
3640 Dummy blocks are not written
3642 if(block->isDummy())
3644 /*v3s16 p = block->getPos();
3645 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3646 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3650 // Format used for writing
3651 u8 version = SER_FMT_VER_HIGHEST;
3653 v3s16 p3d = block->getPos();
3657 v2s16 p2d(p3d.X, p3d.Z);
3658 std::string sectordir = getSectorDir(p2d);
3660 createDirs(sectordir);
3662 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3663 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3664 if(o.good() == false)
3665 throw FileNotGoodException("Cannot open block data");
3668 [0] u8 serialization version
3674 std::ostringstream o(std::ios_base::binary);
3676 o.write((char*)&version, 1);
3679 block->serialize(o, version, true);
3681 // Write block to database
3683 std::string tmp = o.str();
3684 const char *bytes = tmp.c_str();
3686 bool success = true;
3687 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
3688 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3691 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
3692 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3695 int written = sqlite3_step(m_database_write);
3696 if(written != SQLITE_DONE) {
3697 errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3698 <<sqlite3_errmsg(m_database)<<std::endl;
3701 // Make ready for later reuse
3702 sqlite3_reset(m_database_write);
3704 // We just wrote it to the disk so clear modified flag
3706 block->resetModified();
3709 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3711 DSTACK(__FUNCTION_NAME);
3713 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3716 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3717 if(is.good() == false)
3718 throw FileNotGoodException("Cannot open block file");
3720 v3s16 p3d = getBlockPos(sectordir, blockfile);
3721 v2s16 p2d(p3d.X, p3d.Z);
3723 assert(sector->getPos() == p2d);
3725 u8 version = SER_FMT_VER_INVALID;
3726 is.read((char*)&version, 1);
3729 throw SerializationError("ServerMap::loadBlock(): Failed"
3730 " to read MapBlock version");
3732 /*u32 block_size = MapBlock::serializedLength(version);
3733 SharedBuffer<u8> data(block_size);
3734 is.read((char*)*data, block_size);*/
3736 // This will always return a sector because we're the server
3737 //MapSector *sector = emergeSector(p2d);
3739 MapBlock *block = NULL;
3740 bool created_new = false;
3741 block = sector->getBlockNoCreateNoEx(p3d.Y);
3744 block = sector->createBlankBlockNoInsert(p3d.Y);
3749 block->deSerialize(is, version, true);
3751 // If it's a new block, insert it to the map
3753 sector->insertBlock(block);
3756 Save blocks loaded in old format in new format
3759 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3763 // Should be in database now, so delete the old file
3764 fs::RecursiveDelete(fullpath);
3767 // We just loaded it from the disk, so it's up-to-date.
3768 block->resetModified();
3771 catch(SerializationError &e)
3773 infostream<<"WARNING: Invalid block data on disk "
3774 <<"fullpath="<<fullpath
3775 <<" (SerializationError). "
3776 <<"what()="<<e.what()
3778 //" Ignoring. A new one will be generated.
3781 // TODO: Backup file; name is in fullpath.
3785 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3787 DSTACK(__FUNCTION_NAME);
3790 std::istringstream is(*blob, std::ios_base::binary);
3792 u8 version = SER_FMT_VER_INVALID;
3793 is.read((char*)&version, 1);
3796 throw SerializationError("ServerMap::loadBlock(): Failed"
3797 " to read MapBlock version");
3799 /*u32 block_size = MapBlock::serializedLength(version);
3800 SharedBuffer<u8> data(block_size);
3801 is.read((char*)*data, block_size);*/
3803 // This will always return a sector because we're the server
3804 //MapSector *sector = emergeSector(p2d);
3806 MapBlock *block = NULL;
3807 bool created_new = false;
3808 block = sector->getBlockNoCreateNoEx(p3d.Y);
3811 block = sector->createBlankBlockNoInsert(p3d.Y);
3816 block->deSerialize(is, version, true);
3818 // If it's a new block, insert it to the map
3820 sector->insertBlock(block);
3823 Save blocks loaded in old format in new format
3826 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3827 // Only save if asked to; no need to update version
3831 // We just loaded it from, so it's up-to-date.
3832 block->resetModified();
3835 catch(SerializationError &e)
3837 errorstream<<"Invalid block data in database"
3838 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3839 <<" (SerializationError): "<<e.what()<<std::endl;
3841 // TODO: Block should be marked as invalid in memory so that it is
3842 // not touched but the game can run
3844 if(g_settings->getBool("ignore_world_load_errors")){
3845 errorstream<<"Ignoring block load error. Duck and cover! "
3846 <<"(ignore_world_load_errors)"<<std::endl;
3848 throw SerializationError("Invalid block data in database");
3854 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3856 DSTACK(__FUNCTION_NAME);
3858 v2s16 p2d(blockpos.X, blockpos.Z);
3860 if(!loadFromFolders()) {
3863 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3864 infostream<<"WARNING: Could not bind block position for load: "
3865 <<sqlite3_errmsg(m_database)<<std::endl;
3866 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3868 Make sure sector is loaded
3870 MapSector *sector = createSector(p2d);
3875 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3876 size_t len = sqlite3_column_bytes(m_database_read, 0);
3878 std::string datastr(data, len);
3880 loadBlock(&datastr, blockpos, sector, false);
3882 sqlite3_step(m_database_read);
3883 // We should never get more than 1 row, so ok to reset
3884 sqlite3_reset(m_database_read);
3886 return getBlockNoCreateNoEx(blockpos);
3888 sqlite3_reset(m_database_read);
3890 // Not found in database, try the files
3893 // The directory layout we're going to load from.
3894 // 1 - original sectors/xxxxzzzz/
3895 // 2 - new sectors2/xxx/zzz/
3896 // If we load from anything but the latest structure, we will
3897 // immediately save to the new one, and remove the old.
3899 std::string sectordir1 = getSectorDir(p2d, 1);
3900 std::string sectordir;
3901 if(fs::PathExists(sectordir1))
3903 sectordir = sectordir1;
3908 sectordir = getSectorDir(p2d, 2);
3912 Make sure sector is loaded
3914 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3918 sector = loadSectorMeta(sectordir, loadlayout != 2);
3920 catch(InvalidFilenameException &e)
3924 catch(FileNotGoodException &e)
3928 catch(std::exception &e)
3935 Make sure file exists
3938 std::string blockfilename = getBlockFilename(blockpos);
3939 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3943 Load block and save it to the database
3945 loadBlock(sectordir, blockfilename, sector, true);
3946 return getBlockNoCreateNoEx(blockpos);
3949 void ServerMap::PrintInfo(std::ostream &out)
3958 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3963 MapVoxelManipulator::~MapVoxelManipulator()
3965 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3969 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3971 TimeTaker timer1("emerge", &emerge_time);
3973 // Units of these are MapBlocks
3974 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3975 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3977 VoxelArea block_area_nodes
3978 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3980 addArea(block_area_nodes);
3982 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3983 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3984 for(s32 x=p_min.X; x<=p_max.X; x++)
3987 core::map<v3s16, bool>::Node *n;
3988 n = m_loaded_blocks.find(p);
3992 bool block_data_inexistent = false;
3995 TimeTaker timer1("emerge load", &emerge_load_time);
3997 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3998 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4000 a.print(infostream);
4001 infostream<<std::endl;*/
4003 MapBlock *block = m_map->getBlockNoCreate(p);
4004 if(block->isDummy())
4005 block_data_inexistent = true;
4007 block->copyTo(*this);
4009 catch(InvalidPositionException &e)
4011 block_data_inexistent = true;
4014 if(block_data_inexistent)
4016 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4017 // Fill with VOXELFLAG_INEXISTENT
4018 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4019 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4021 s32 i = m_area.index(a.MinEdge.X,y,z);
4022 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4026 m_loaded_blocks.insert(p, !block_data_inexistent);
4029 //infostream<<"emerge done"<<std::endl;
4033 SUGG: Add an option to only update eg. water and air nodes.
4034 This will make it interfere less with important stuff if
4037 void MapVoxelManipulator::blitBack
4038 (core::map<v3s16, MapBlock*> & modified_blocks)
4040 if(m_area.getExtent() == v3s16(0,0,0))
4043 //TimeTaker timer1("blitBack");
4045 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4046 <<m_loaded_blocks.size()<<std::endl;*/
4049 Initialize block cache
4051 v3s16 blockpos_last;
4052 MapBlock *block = NULL;
4053 bool block_checked_in_modified = false;
4055 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4056 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4057 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4061 u8 f = m_flags[m_area.index(p)];
4062 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4065 MapNode &n = m_data[m_area.index(p)];
4067 v3s16 blockpos = getNodeBlockPos(p);
4072 if(block == NULL || blockpos != blockpos_last){
4073 block = m_map->getBlockNoCreate(blockpos);
4074 blockpos_last = blockpos;
4075 block_checked_in_modified = false;
4078 // Calculate relative position in block
4079 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4081 // Don't continue if nothing has changed here
4082 if(block->getNode(relpos) == n)
4085 //m_map->setNode(m_area.MinEdge + p, n);
4086 block->setNode(relpos, n);
4089 Make sure block is in modified_blocks
4091 if(block_checked_in_modified == false)
4093 modified_blocks[blockpos] = block;
4094 block_checked_in_modified = true;
4097 catch(InvalidPositionException &e)
4103 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4104 MapVoxelManipulator(map),
4105 m_create_area(false)
4109 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4113 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4115 // Just create the area so that it can be pointed to
4116 VoxelManipulator::emerge(a, caller_id);
4119 void ManualMapVoxelManipulator::initialEmerge(
4120 v3s16 blockpos_min, v3s16 blockpos_max)
4122 TimeTaker timer1("initialEmerge", &emerge_time);
4124 // Units of these are MapBlocks
4125 v3s16 p_min = blockpos_min;
4126 v3s16 p_max = blockpos_max;
4128 VoxelArea block_area_nodes
4129 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4131 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4134 infostream<<"initialEmerge: area: ";
4135 block_area_nodes.print(infostream);
4136 infostream<<" ("<<size_MB<<"MB)";
4137 infostream<<std::endl;
4140 addArea(block_area_nodes);
4142 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4143 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4144 for(s32 x=p_min.X; x<=p_max.X; x++)
4147 core::map<v3s16, bool>::Node *n;
4148 n = m_loaded_blocks.find(p);
4152 bool block_data_inexistent = false;
4155 TimeTaker timer1("emerge load", &emerge_load_time);
4157 MapBlock *block = m_map->getBlockNoCreate(p);
4158 if(block->isDummy())
4159 block_data_inexistent = true;
4161 block->copyTo(*this);
4163 catch(InvalidPositionException &e)
4165 block_data_inexistent = true;
4168 if(block_data_inexistent)
4171 Mark area inexistent
4173 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4174 // Fill with VOXELFLAG_INEXISTENT
4175 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4176 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4178 s32 i = m_area.index(a.MinEdge.X,y,z);
4179 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4183 m_loaded_blocks.insert(p, !block_data_inexistent);
4187 void ManualMapVoxelManipulator::blitBackAll(
4188 core::map<v3s16, MapBlock*> * modified_blocks)
4190 if(m_area.getExtent() == v3s16(0,0,0))
4194 Copy data of all blocks
4196 for(core::map<v3s16, bool>::Iterator
4197 i = m_loaded_blocks.getIterator();
4198 i.atEnd() == false; i++)
4200 v3s16 p = i.getNode()->getKey();
4201 bool existed = i.getNode()->getValue();
4202 if(existed == false)
4204 // The Great Bug was found using this
4205 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4206 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4210 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4213 infostream<<"WARNING: "<<__FUNCTION_NAME
4214 <<": got NULL block "
4215 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4220 block->copyFrom(*this);
4223 modified_blocks->insert(p, block);