LCOV - code coverage report
Current view: top level - src - map.cpp (source / functions) Hit Total Coverage
Test: report Lines: 339 1442 23.5 %
Date: 2015-07-11 18:23:49 Functions: 23 90 25.6 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       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.
       9             : 
      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.
      14             : 
      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.
      18             : */
      19             : 
      20             : #include "map.h"
      21             : #include "mapsector.h"
      22             : #include "mapblock.h"
      23             : #include "filesys.h"
      24             : #include "voxel.h"
      25             : #include "porting.h"
      26             : #include "serialization.h"
      27             : #include "nodemetadata.h"
      28             : #include "settings.h"
      29             : #include "log.h"
      30             : #include "profiler.h"
      31             : #include "nodedef.h"
      32             : #include "gamedef.h"
      33             : #include "util/directiontables.h"
      34             : #include "util/mathconstants.h"
      35             : #include "rollback_interface.h"
      36             : #include "environment.h"
      37             : #include "emerge.h"
      38             : #include "mapgen_v6.h"
      39             : #include "mg_biome.h"
      40             : #include "config.h"
      41             : #include "server.h"
      42             : #include "database.h"
      43             : #include "database-dummy.h"
      44             : #include "database-sqlite3.h"
      45             : #include <deque>
      46             : #if USE_LEVELDB
      47             : #include "database-leveldb.h"
      48             : #endif
      49             : #if USE_REDIS
      50             : #include "database-redis.h"
      51             : #endif
      52             : 
      53             : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
      54             : 
      55             : 
      56             : /*
      57             :         Map
      58             : */
      59             : 
      60           1 : Map::Map(std::ostream &dout, IGameDef *gamedef):
      61             :         m_dout(dout),
      62             :         m_gamedef(gamedef),
      63             :         m_sector_cache(NULL),
      64             :         m_transforming_liquid_loop_count_multiplier(1.0f),
      65             :         m_unprocessed_count(0),
      66             :         m_inc_trending_up_start_time(0),
      67           1 :         m_queue_size_timer_started(false)
      68             : {
      69           1 : }
      70             : 
      71           2 : Map::~Map()
      72             : {
      73             :         /*
      74             :                 Free all MapSectors
      75             :         */
      76         735 :         for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
      77         490 :                 i != m_sectors.end(); ++i)
      78             :         {
      79         244 :                 delete i->second;
      80             :         }
      81           1 : }
      82             : 
      83           0 : void Map::addEventReceiver(MapEventReceiver *event_receiver)
      84             : {
      85           0 :         m_event_receivers.insert(event_receiver);
      86           0 : }
      87             : 
      88           0 : void Map::removeEventReceiver(MapEventReceiver *event_receiver)
      89             : {
      90           0 :         m_event_receivers.erase(event_receiver);
      91           0 : }
      92             : 
      93           0 : void Map::dispatchEvent(MapEditEvent *event)
      94             : {
      95           0 :         for(std::set<MapEventReceiver*>::iterator
      96           0 :                         i = m_event_receivers.begin();
      97           0 :                         i != m_event_receivers.end(); ++i)
      98             :         {
      99           0 :                 (*i)->onMapEditEvent(event);
     100             :         }
     101           0 : }
     102             : 
     103     2350001 : MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
     104             : {
     105     2350001 :         if(m_sector_cache != NULL && p == m_sector_cache_p){
     106     1822174 :                 MapSector * sector = m_sector_cache;
     107     1822174 :                 return sector;
     108             :         }
     109             : 
     110      527827 :         std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
     111             : 
     112      527827 :         if(n == m_sectors.end())
     113       52096 :                 return NULL;
     114             : 
     115      475731 :         MapSector *sector = n->second;
     116             : 
     117             :         // Cache the last result
     118      475731 :         m_sector_cache_p = p;
     119      475731 :         m_sector_cache = sector;
     120             : 
     121      475731 :         return sector;
     122             : }
     123             : 
     124     2350001 : MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
     125             : {
     126     2350001 :         return getSectorNoGenerateNoExNoLock(p);
     127             : }
     128             : 
     129         786 : MapSector * Map::getSectorNoGenerate(v2s16 p)
     130             : {
     131         786 :         MapSector *sector = getSectorNoGenerateNoEx(p);
     132         786 :         if(sector == NULL)
     133         244 :                 throw InvalidPositionException();
     134             : 
     135         542 :         return sector;
     136             : }
     137             : 
     138     2349215 : MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
     139             : {
     140     2349215 :         v2s16 p2d(p3d.X, p3d.Z);
     141     2349215 :         MapSector * sector = getSectorNoGenerateNoEx(p2d);
     142     2349215 :         if(sector == NULL)
     143       51852 :                 return NULL;
     144     2297363 :         MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
     145     2297363 :         return block;
     146             : }
     147             : 
     148        7988 : MapBlock * Map::getBlockNoCreate(v3s16 p3d)
     149             : {
     150        7988 :         MapBlock *block = getBlockNoCreateNoEx(p3d);
     151        7988 :         if(block == NULL)
     152           0 :                 throw InvalidPositionException();
     153        7988 :         return block;
     154             : }
     155             : 
     156           0 : bool Map::isNodeUnderground(v3s16 p)
     157             : {
     158           0 :         v3s16 blockpos = getNodeBlockPos(p);
     159             :         try{
     160           0 :                 MapBlock * block = getBlockNoCreate(blockpos);
     161           0 :                 return block->getIsUnderground();
     162             :         }
     163           0 :         catch(InvalidPositionException &e)
     164             :         {
     165           0 :                 return false;
     166             :         }
     167             : }
     168             : 
     169           0 : bool Map::isValidPosition(v3s16 p)
     170             : {
     171           0 :         v3s16 blockpos = getNodeBlockPos(p);
     172           0 :         MapBlock *block = getBlockNoCreate(blockpos);
     173           0 :         return (block != NULL);
     174             : }
     175             : 
     176             : // Returns a CONTENT_IGNORE node if not found
     177     2239998 : MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
     178             : {
     179     2239998 :         v3s16 blockpos = getNodeBlockPos(p);
     180     2239998 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
     181     2239998 :         if (block == NULL) {
     182      139569 :                 if (is_valid_position != NULL)
     183       87061 :                         *is_valid_position = false;
     184      139569 :                 return MapNode(CONTENT_IGNORE);
     185             :         }
     186             : 
     187     2100429 :         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
     188             :         bool is_valid_p;
     189     2100429 :         MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
     190     2100429 :         if (is_valid_position != NULL)
     191     1078063 :                 *is_valid_position = is_valid_p;
     192     2100429 :         return node;
     193             : }
     194             : 
     195             : #if 0
     196             : // Deprecated
     197             : // throws InvalidPositionException if not found
     198             : // TODO: Now this is deprecated, getNodeNoEx should be renamed
     199             : MapNode Map::getNode(v3s16 p)
     200             : {
     201             :         v3s16 blockpos = getNodeBlockPos(p);
     202             :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
     203             :         if (block == NULL)
     204             :                 throw InvalidPositionException();
     205             :         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
     206             :         bool is_valid_position;
     207             :         MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
     208             :         if (!is_valid_position)
     209             :                 throw InvalidPositionException();
     210             :         return node;
     211             : }
     212             : #endif
     213             : 
     214             : // throws InvalidPositionException if not found
     215          56 : void Map::setNode(v3s16 p, MapNode & n)
     216             : {
     217          56 :         v3s16 blockpos = getNodeBlockPos(p);
     218          56 :         MapBlock *block = getBlockNoCreate(blockpos);
     219          56 :         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
     220             :         // Never allow placing CONTENT_IGNORE, it fucks up stuff
     221          56 :         if(n.getContent() == CONTENT_IGNORE){
     222             :                 bool temp_bool;
     223           0 :                 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
     224           0 :                                 <<" while trying to replace \""
     225           0 :                                 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
     226           0 :                                 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
     227           0 :                 debug_stacks_print_to(infostream);
     228           0 :                 return;
     229             :         }
     230          56 :         block->setNodeNoCheck(relpos, n);
     231             : }
     232             : 
     233             : 
     234             : /*
     235             :         Goes recursively through the neighbours of the node.
     236             : 
     237             :         Alters only transparent nodes.
     238             : 
     239             :         If the lighting of the neighbour is lower than the lighting of
     240             :         the node was (before changing it to 0 at the step before), the
     241             :         lighting of the neighbour is set to 0 and then the same stuff
     242             :         repeats for the neighbour.
     243             : 
     244             :         The ending nodes of the routine are stored in light_sources.
     245             :         This is useful when a light is removed. In such case, this
     246             :         routine can be called for the light node and then again for
     247             :         light_sources to re-light the area without the removed light.
     248             : 
     249             :         values of from_nodes are lighting values.
     250             : */
     251         240 : void Map::unspreadLight(enum LightBank bank,
     252             :                 std::map<v3s16, u8> & from_nodes,
     253             :                 std::set<v3s16> & light_sources,
     254             :                 std::map<v3s16, MapBlock*>  & modified_blocks)
     255             : {
     256         240 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     257             : 
     258             :         v3s16 dirs[6] = {
     259             :                 v3s16(0,0,1), // back
     260             :                 v3s16(0,1,0), // top
     261             :                 v3s16(1,0,0), // right
     262             :                 v3s16(0,0,-1), // front
     263             :                 v3s16(0,-1,0), // bottom
     264             :                 v3s16(-1,0,0), // left
     265         240 :         };
     266             : 
     267         240 :         if(from_nodes.empty())
     268           0 :                 return;
     269             : 
     270         240 :         u32 blockchangecount = 0;
     271             : 
     272         480 :         std::map<v3s16, u8> unlighted_nodes;
     273             : 
     274             :         /*
     275             :                 Initialize block cache
     276             :         */
     277         240 :         v3s16 blockpos_last;
     278         240 :         MapBlock *block = NULL;
     279             :         // Cache this a bit, too
     280         240 :         bool block_checked_in_modified = false;
     281             : 
     282        2322 :         for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
     283        1548 :                 j != from_nodes.end(); ++j)
     284             :         {
     285         534 :                 v3s16 pos = j->first;
     286         534 :                 v3s16 blockpos = getNodeBlockPos(pos);
     287             : 
     288             :                 // Only fetch a new block if the block position has changed
     289             :                 try{
     290         534 :                         if(block == NULL || blockpos != blockpos_last){
     291         442 :                                 block = getBlockNoCreate(blockpos);
     292         442 :                                 blockpos_last = blockpos;
     293             : 
     294         442 :                                 block_checked_in_modified = false;
     295         442 :                                 blockchangecount++;
     296             :                         }
     297             :                 }
     298           0 :                 catch(InvalidPositionException &e)
     299             :                 {
     300           0 :                         continue;
     301             :                 }
     302             : 
     303         534 :                 if(block->isDummy())
     304           0 :                         continue;
     305             : 
     306             :                 // Calculate relative position in block
     307             :                 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
     308             : 
     309             :                 // Get node straight from the block
     310             :                 //MapNode n = block->getNode(relpos);
     311             : 
     312         534 :                 u8 oldlight = j->second;
     313             : 
     314             :                 // Loop through 6 neighbors
     315        3738 :                 for(u16 i=0; i<6; i++)
     316             :                 {
     317             :                         // Get the position of the neighbor node
     318        3204 :                         v3s16 n2pos = pos + dirs[i];
     319             : 
     320             :                         // Get the block where the node is located
     321        3204 :                         v3s16 blockpos, relpos;
     322        3204 :                         getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
     323             : 
     324             :                         // Only fetch a new block if the block position has changed
     325             :                         try {
     326        3204 :                                 if(block == NULL || blockpos != blockpos_last){
     327        1162 :                                         block = getBlockNoCreate(blockpos);
     328        1162 :                                         blockpos_last = blockpos;
     329             : 
     330        1162 :                                         block_checked_in_modified = false;
     331        1162 :                                         blockchangecount++;
     332             :                                 }
     333             :                         }
     334           0 :                         catch(InvalidPositionException &e) {
     335           0 :                                 continue;
     336             :                         }
     337             : 
     338             :                         // Get node straight from the block
     339             :                         bool is_valid_position;
     340        3204 :                         MapNode n2 = block->getNode(relpos, &is_valid_position);
     341        3204 :                         if (!is_valid_position)
     342           0 :                                 continue;
     343             : 
     344        3204 :                         bool changed = false;
     345             : 
     346             :                         //TODO: Optimize output by optimizing light_sources?
     347             : 
     348             :                         /*
     349             :                                 If the neighbor is dimmer than what was specified
     350             :                                 as oldlight (the light of the previous node)
     351             :                         */
     352        3204 :                         if(n2.getLight(bank, nodemgr) < oldlight)
     353             :                         {
     354             :                                 /*
     355             :                                         And the neighbor is transparent and it has some light
     356             :                                 */
     357        4162 :                                 if(nodemgr->get(n2).light_propagates
     358        2081 :                                                 && n2.getLight(bank, nodemgr) != 0)
     359             :                                 {
     360             :                                         /*
     361             :                                                 Set light to 0 and add to queue
     362             :                                         */
     363             : 
     364         424 :                                         u8 current_light = n2.getLight(bank, nodemgr);
     365         424 :                                         n2.setLight(bank, 0, nodemgr);
     366         424 :                                         block->setNode(relpos, n2);
     367             : 
     368         424 :                                         unlighted_nodes[n2pos] = current_light;
     369         424 :                                         changed = true;
     370             : 
     371             :                                         /*
     372             :                                                 Remove from light_sources if it is there
     373             :                                                 NOTE: This doesn't happen nearly at all
     374             :                                         */
     375             :                                         /*if(light_sources.find(n2pos))
     376             :                                         {
     377             :                                                 infostream<<"Removed from light_sources"<<std::endl;
     378             :                                                 light_sources.remove(n2pos);
     379             :                                         }*/
     380             :                                 }
     381             : 
     382             :                                 /*// DEBUG
     383             :                                 if(light_sources.find(n2pos) != NULL)
     384             :                                         light_sources.remove(n2pos);*/
     385             :                         }
     386             :                         else{
     387        1123 :                                 light_sources.insert(n2pos);
     388             :                         }
     389             : 
     390             :                         // Add to modified_blocks
     391        3204 :                         if(changed == true && block_checked_in_modified == false)
     392             :                         {
     393             :                                 // If the block is not found in modified_blocks, add.
     394         378 :                                 if(modified_blocks.find(blockpos) == modified_blocks.end())
     395             :                                 {
     396          33 :                                         modified_blocks[blockpos] = block;
     397             :                                 }
     398         378 :                                 block_checked_in_modified = true;
     399             :                         }
     400             :                 }
     401             :         }
     402             : 
     403             :         /*infostream<<"unspreadLight(): Changed block "
     404             :         <<blockchangecount<<" times"
     405             :         <<" for "<<from_nodes.size()<<" nodes"
     406             :         <<std::endl;*/
     407             : 
     408         240 :         if(!unlighted_nodes.empty())
     409         130 :                 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
     410             : }
     411             : 
     412             : /*
     413             :         A single-node wrapper of the above
     414             : */
     415         110 : void Map::unLightNeighbors(enum LightBank bank,
     416             :                 v3s16 pos, u8 lightwas,
     417             :                 std::set<v3s16> & light_sources,
     418             :                 std::map<v3s16, MapBlock*>  & modified_blocks)
     419             : {
     420         220 :         std::map<v3s16, u8> from_nodes;
     421         110 :         from_nodes[pos] = lightwas;
     422             : 
     423         110 :         unspreadLight(bank, from_nodes, light_sources, modified_blocks);
     424         110 : }
     425             : 
     426             : /*
     427             :         Lights neighbors of from_nodes, collects all them and then
     428             :         goes on recursively.
     429             : */
     430         306 : void Map::spreadLight(enum LightBank bank,
     431             :                 std::set<v3s16> & from_nodes,
     432             :                 std::map<v3s16, MapBlock*> & modified_blocks)
     433             : {
     434         306 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     435             : 
     436             :         const v3s16 dirs[6] = {
     437             :                 v3s16(0,0,1), // back
     438             :                 v3s16(0,1,0), // top
     439             :                 v3s16(1,0,0), // right
     440             :                 v3s16(0,0,-1), // front
     441             :                 v3s16(0,-1,0), // bottom
     442             :                 v3s16(-1,0,0), // left
     443         306 :         };
     444             : 
     445         306 :         if(from_nodes.empty())
     446           0 :                 return;
     447             : 
     448         306 :         u32 blockchangecount = 0;
     449             : 
     450         612 :         std::set<v3s16> lighted_nodes;
     451             : 
     452             :         /*
     453             :                 Initialize block cache
     454             :         */
     455         306 :         v3s16 blockpos_last;
     456         306 :         MapBlock *block = NULL;
     457             :                 // Cache this a bit, too
     458         306 :         bool block_checked_in_modified = false;
     459             : 
     460        9744 :         for(std::set<v3s16>::iterator j = from_nodes.begin();
     461        6496 :                 j != from_nodes.end(); ++j)
     462             :         {
     463        2942 :                 v3s16 pos = *j;
     464        2942 :                 v3s16 blockpos, relpos;
     465             : 
     466        2942 :                 getNodeBlockPosWithOffset(pos, blockpos, relpos);
     467             : 
     468             :                 // Only fetch a new block if the block position has changed
     469             :                 try {
     470        2942 :                         if(block == NULL || blockpos != blockpos_last){
     471        1242 :                                 block = getBlockNoCreate(blockpos);
     472        1242 :                                 blockpos_last = blockpos;
     473             : 
     474        1242 :                                 block_checked_in_modified = false;
     475        1242 :                                 blockchangecount++;
     476             :                         }
     477             :                 }
     478           0 :                 catch(InvalidPositionException &e) {
     479           0 :                         continue;
     480             :                 }
     481             : 
     482        2942 :                 if(block->isDummy())
     483           0 :                         continue;
     484             : 
     485             :                 // Get node straight from the block
     486             :                 bool is_valid_position;
     487        2942 :                 MapNode n = block->getNode(relpos, &is_valid_position);
     488             : 
     489        2942 :                 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
     490        2942 :                 u8 newlight = diminish_light(oldlight);
     491             : 
     492             :                 // Loop through 6 neighbors
     493       20594 :                 for(u16 i=0; i<6; i++){
     494             :                         // Get the position of the neighbor node
     495       17652 :                         v3s16 n2pos = pos + dirs[i];
     496             : 
     497             :                         // Get the block where the node is located
     498       17652 :                         v3s16 blockpos, relpos;
     499       17652 :                         getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
     500             : 
     501             :                         // Only fetch a new block if the block position has changed
     502             :                         try {
     503       17652 :                                 if(block == NULL || blockpos != blockpos_last){
     504        4972 :                                         block = getBlockNoCreate(blockpos);
     505        4972 :                                         blockpos_last = blockpos;
     506             : 
     507        4972 :                                         block_checked_in_modified = false;
     508        4972 :                                         blockchangecount++;
     509             :                                 }
     510             :                         }
     511           0 :                         catch(InvalidPositionException &e) {
     512           0 :                                 continue;
     513             :                         }
     514             : 
     515             :                         // Get node straight from the block
     516       17652 :                         MapNode n2 = block->getNode(relpos, &is_valid_position);
     517       17652 :                         if (!is_valid_position)
     518           0 :                                 continue;
     519             : 
     520       17652 :                         bool changed = false;
     521             :                         /*
     522             :                                 If the neighbor is brighter than the current node,
     523             :                                 add to list (it will light up this node on its turn)
     524             :                         */
     525       17652 :                         if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
     526             :                         {
     527         776 :                                 lighted_nodes.insert(n2pos);
     528         776 :                                 changed = true;
     529             :                         }
     530             :                         /*
     531             :                                 If the neighbor is dimmer than how much light this node
     532             :                                 would spread on it, add to list
     533             :                         */
     534       17652 :                         if(n2.getLight(bank, nodemgr) < newlight)
     535             :                         {
     536        2968 :                                 if(nodemgr->get(n2).light_propagates)
     537             :                                 {
     538         532 :                                         n2.setLight(bank, newlight, nodemgr);
     539         532 :                                         block->setNode(relpos, n2);
     540         532 :                                         lighted_nodes.insert(n2pos);
     541         532 :                                         changed = true;
     542             :                                 }
     543             :                         }
     544             : 
     545             :                         // Add to modified_blocks
     546       17652 :                         if(changed == true && block_checked_in_modified == false)
     547             :                         {
     548             :                                 // If the block is not found in modified_blocks, add.
     549         854 :                                 if(modified_blocks.find(blockpos) == modified_blocks.end())
     550             :                                 {
     551          26 :                                         modified_blocks[blockpos] = block;
     552             :                                 }
     553         854 :                                 block_checked_in_modified = true;
     554             :                         }
     555             :                 }
     556             :         }
     557             : 
     558             :         /*infostream<<"spreadLight(): Changed block "
     559             :                         <<blockchangecount<<" times"
     560             :                         <<" for "<<from_nodes.size()<<" nodes"
     561             :                         <<std::endl;*/
     562             : 
     563         306 :         if(!lighted_nodes.empty())
     564         179 :                 spreadLight(bank, lighted_nodes, modified_blocks);
     565             : }
     566             : 
     567             : /*
     568             :         A single-node source variation of the above.
     569             : */
     570          17 : void Map::lightNeighbors(enum LightBank bank,
     571             :                 v3s16 pos,
     572             :                 std::map<v3s16, MapBlock*> & modified_blocks)
     573             : {
     574          34 :         std::set<v3s16> from_nodes;
     575          17 :         from_nodes.insert(pos);
     576          17 :         spreadLight(bank, from_nodes, modified_blocks);
     577          17 : }
     578             : 
     579          12 : v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
     580             : {
     581          12 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     582             : 
     583             :         v3s16 dirs[6] = {
     584             :                 v3s16(0,0,1), // back
     585             :                 v3s16(0,1,0), // top
     586             :                 v3s16(1,0,0), // right
     587             :                 v3s16(0,0,-1), // front
     588             :                 v3s16(0,-1,0), // bottom
     589             :                 v3s16(-1,0,0), // left
     590          12 :         };
     591             : 
     592          12 :         u8 brightest_light = 0;
     593          12 :         v3s16 brightest_pos(0,0,0);
     594          12 :         bool found_something = false;
     595             : 
     596             :         // Loop through 6 neighbors
     597          84 :         for(u16 i=0; i<6; i++){
     598             :                 // Get the position of the neighbor node
     599          72 :                 v3s16 n2pos = p + dirs[i];
     600          72 :                 MapNode n2;
     601             :                 bool is_valid_position;
     602          72 :                 n2 = getNodeNoEx(n2pos, &is_valid_position);
     603          72 :                 if (!is_valid_position)
     604           0 :                         continue;
     605             : 
     606          72 :                 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
     607          17 :                         brightest_light = n2.getLight(bank, nodemgr);
     608          17 :                         brightest_pos = n2pos;
     609          17 :                         found_something = true;
     610             :                 }
     611             :         }
     612             : 
     613          12 :         if(found_something == false)
     614           0 :                 throw InvalidPositionException();
     615             : 
     616          12 :         return brightest_pos;
     617             : }
     618             : 
     619             : /*
     620             :         Propagates sunlight down from a node.
     621             :         Starting point gets sunlight.
     622             : 
     623             :         Returns the lowest y value of where the sunlight went.
     624             : 
     625             :         Mud is turned into grass in where the sunlight stops.
     626             : */
     627           5 : s16 Map::propagateSunlight(v3s16 start,
     628             :                 std::map<v3s16, MapBlock*> & modified_blocks)
     629             : {
     630           5 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     631             : 
     632           5 :         s16 y = start.Y;
     633           5 :         for(; ; y--)
     634             :         {
     635          10 :                 v3s16 pos(start.X, y, start.Z);
     636             : 
     637          10 :                 v3s16 blockpos = getNodeBlockPos(pos);
     638             :                 MapBlock *block;
     639             :                 try{
     640          10 :                         block = getBlockNoCreate(blockpos);
     641             :                 }
     642           0 :                 catch(InvalidPositionException &e)
     643             :                 {
     644           0 :                         break;
     645             :                 }
     646             : 
     647          10 :                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
     648             :                 bool is_valid_position;
     649          10 :                 MapNode n = block->getNode(relpos, &is_valid_position);
     650          10 :                 if (!is_valid_position)
     651           0 :                         break;
     652             : 
     653          10 :                 if(nodemgr->get(n).sunlight_propagates)
     654             :                 {
     655           5 :                         n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
     656           5 :                         block->setNode(relpos, n);
     657             : 
     658           5 :                         modified_blocks[blockpos] = block;
     659             :                 }
     660             :                 else
     661             :                 {
     662             :                         // Sunlight goes no further
     663           5 :                         break;
     664             :                 }
     665             :         }
     666           5 :         return y + 1;
     667             : }
     668             : 
     669           0 : void Map::updateLighting(enum LightBank bank,
     670             :                 std::map<v3s16, MapBlock*> & a_blocks,
     671             :                 std::map<v3s16, MapBlock*> & modified_blocks)
     672             : {
     673           0 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     674             : 
     675             :         /*m_dout<<DTIME<<"Map::updateLighting(): "
     676             :                         <<a_blocks.size()<<" blocks."<<std::endl;*/
     677             : 
     678             :         //TimeTaker timer("updateLighting");
     679             : 
     680             :         // For debugging
     681             :         //bool debug=true;
     682             :         //u32 count_was = modified_blocks.size();
     683             : 
     684             :         //std::map<v3s16, MapBlock*> blocks_to_update;
     685             : 
     686           0 :         std::set<v3s16> light_sources;
     687             : 
     688           0 :         std::map<v3s16, u8> unlight_from;
     689             : 
     690           0 :         int num_bottom_invalid = 0;
     691             : 
     692             :         {
     693             :         //TimeTaker t("first stuff");
     694             : 
     695           0 :         for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
     696           0 :                 i != a_blocks.end(); ++i)
     697             :         {
     698           0 :                 MapBlock *block = i->second;
     699             : 
     700           0 :                 for(;;)
     701             :                 {
     702             :                         // Don't bother with dummy blocks.
     703           0 :                         if(block->isDummy())
     704           0 :                                 break;
     705             : 
     706           0 :                         v3s16 pos = block->getPos();
     707           0 :                         v3s16 posnodes = block->getPosRelative();
     708           0 :                         modified_blocks[pos] = block;
     709             :                         //blocks_to_update[pos] = block;
     710             : 
     711             :                         /*
     712             :                                 Clear all light from block
     713             :                         */
     714           0 :                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
     715           0 :                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
     716           0 :                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
     717             :                         {
     718           0 :                                 v3s16 p(x,y,z);
     719             :                                 bool is_valid_position;
     720           0 :                                 MapNode n = block->getNode(p, &is_valid_position);
     721           0 :                                 if (!is_valid_position) {
     722             :                                         /* This would happen when dealing with a
     723             :                                            dummy block.
     724             :                                         */
     725           0 :                                         infostream<<"updateLighting(): InvalidPositionException"
     726           0 :                                                         <<std::endl;
     727           0 :                                         continue;
     728             :                                 }
     729           0 :                                 u8 oldlight = n.getLight(bank, nodemgr);
     730           0 :                                 n.setLight(bank, 0, nodemgr);
     731           0 :                                 block->setNode(p, n);
     732             : 
     733             :                                 // If node sources light, add to list
     734           0 :                                 u8 source = nodemgr->get(n).light_source;
     735           0 :                                 if(source != 0)
     736           0 :                                         light_sources.insert(p + posnodes);
     737             : 
     738             :                                 // Collect borders for unlighting
     739           0 :                                 if((x==0 || x == MAP_BLOCKSIZE-1
     740           0 :                                                 || y==0 || y == MAP_BLOCKSIZE-1
     741           0 :                                                 || z==0 || z == MAP_BLOCKSIZE-1)
     742           0 :                                                 && oldlight != 0)
     743             :                                 {
     744           0 :                                         v3s16 p_map = p + posnodes;
     745           0 :                                         unlight_from[p_map] = oldlight;
     746             :                                 }
     747             : 
     748             : 
     749             :                         }
     750             : 
     751           0 :                         if(bank == LIGHTBANK_DAY)
     752             :                         {
     753           0 :                                 bool bottom_valid = block->propagateSunlight(light_sources);
     754             : 
     755           0 :                                 if(!bottom_valid)
     756           0 :                                         num_bottom_invalid++;
     757             : 
     758             :                                 // If bottom is valid, we're done.
     759           0 :                                 if(bottom_valid)
     760           0 :                                         break;
     761             :                         }
     762           0 :                         else if(bank == LIGHTBANK_NIGHT)
     763             :                         {
     764             :                                 // For night lighting, sunlight is not propagated
     765           0 :                                 break;
     766             :                         }
     767             :                         else
     768             :                         {
     769             :                                 assert("Invalid lighting bank" == NULL);
     770             :                         }
     771             : 
     772             :                         /*infostream<<"Bottom for sunlight-propagated block ("
     773             :                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
     774             :                                         <<std::endl;*/
     775             : 
     776             :                         // Bottom sunlight is not valid; get the block and loop to it
     777             : 
     778           0 :                         pos.Y--;
     779             :                         try{
     780           0 :                                 block = getBlockNoCreate(pos);
     781             :                         }
     782           0 :                         catch(InvalidPositionException &e)
     783             :                         {
     784           0 :                                 FATAL_ERROR("Invalid position");
     785             :                         }
     786             : 
     787             :                 }
     788             :         }
     789             : 
     790             :         }
     791             : 
     792             :         /*
     793             :                 Enable this to disable proper lighting for speeding up map
     794             :                 generation for testing or whatever
     795             :         */
     796             : #if 0
     797             :         //if(g_settings->get(""))
     798             :         {
     799             :                 core::map<v3s16, MapBlock*>::Iterator i;
     800             :                 i = blocks_to_update.getIterator();
     801             :                 for(; i.atEnd() == false; i++)
     802             :                 {
     803             :                         MapBlock *block = i.getNode()->getValue();
     804             :                         v3s16 p = block->getPos();
     805             :                         block->setLightingExpired(false);
     806             :                 }
     807             :                 return;
     808             :         }
     809             : #endif
     810             : 
     811             : #if 1
     812             :         {
     813             :                 //TimeTaker timer("unspreadLight");
     814           0 :                 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
     815             :         }
     816             : 
     817             :         /*if(debug)
     818             :         {
     819             :                 u32 diff = modified_blocks.size() - count_was;
     820             :                 count_was = modified_blocks.size();
     821             :                 infostream<<"unspreadLight modified "<<diff<<std::endl;
     822             :         }*/
     823             : 
     824             :         {
     825             :                 //TimeTaker timer("spreadLight");
     826           0 :                 spreadLight(bank, light_sources, modified_blocks);
     827             :         }
     828             : 
     829             :         /*if(debug)
     830             :         {
     831             :                 u32 diff = modified_blocks.size() - count_was;
     832             :                 count_was = modified_blocks.size();
     833             :                 infostream<<"spreadLight modified "<<diff<<std::endl;
     834             :         }*/
     835             : #endif
     836             : 
     837             : #if 0
     838             :         {
     839             :                 //MapVoxelManipulator vmanip(this);
     840             : 
     841             :                 // Make a manual voxel manipulator and load all the blocks
     842             :                 // that touch the requested blocks
     843             :                 ManualMapVoxelManipulator vmanip(this);
     844             : 
     845             :                 {
     846             :                 //TimeTaker timer("initialEmerge");
     847             : 
     848             :                 core::map<v3s16, MapBlock*>::Iterator i;
     849             :                 i = blocks_to_update.getIterator();
     850             :                 for(; i.atEnd() == false; i++)
     851             :                 {
     852             :                         MapBlock *block = i.getNode()->getValue();
     853             :                         v3s16 p = block->getPos();
     854             : 
     855             :                         // Add all surrounding blocks
     856             :                         vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
     857             : 
     858             :                         /*
     859             :                                 Add all surrounding blocks that have up-to-date lighting
     860             :                                 NOTE: This doesn't quite do the job (not everything
     861             :                                           appropriate is lighted)
     862             :                         */
     863             :                         /*for(s16 z=-1; z<=1; z++)
     864             :                         for(s16 y=-1; y<=1; y++)
     865             :                         for(s16 x=-1; x<=1; x++)
     866             :                         {
     867             :                                 v3s16 p2 = p + v3s16(x,y,z);
     868             :                                 MapBlock *block = getBlockNoCreateNoEx(p2);
     869             :                                 if(block == NULL)
     870             :                                         continue;
     871             :                                 if(block->isDummy())
     872             :                                         continue;
     873             :                                 if(block->getLightingExpired())
     874             :                                         continue;
     875             :                                 vmanip.initialEmerge(p2, p2);
     876             :                         }*/
     877             : 
     878             :                         // Lighting of block will be updated completely
     879             :                         block->setLightingExpired(false);
     880             :                 }
     881             :                 }
     882             : 
     883             :                 {
     884             :                         //TimeTaker timer("unSpreadLight");
     885             :                         vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
     886             :                 }
     887             :                 {
     888             :                         //TimeTaker timer("spreadLight");
     889             :                         vmanip.spreadLight(bank, light_sources, nodemgr);
     890             :                 }
     891             :                 {
     892             :                         //TimeTaker timer("blitBack");
     893             :                         vmanip.blitBack(modified_blocks);
     894             :                 }
     895             :                 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
     896             :                 emerge_time = 0;*/
     897             :         }
     898             : #endif
     899             : 
     900             :         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
     901           0 : }
     902             : 
     903           0 : void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
     904             :                 std::map<v3s16, MapBlock*> & modified_blocks)
     905             : {
     906           0 :         updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
     907           0 :         updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
     908             : 
     909             :         /*
     910             :                 Update information about whether day and night light differ
     911             :         */
     912           0 :         for(std::map<v3s16, MapBlock*>::iterator
     913           0 :                         i = modified_blocks.begin();
     914           0 :                         i != modified_blocks.end(); ++i)
     915             :         {
     916           0 :                 MapBlock *block = i->second;
     917           0 :                 block->expireDayNightDiff();
     918             :         }
     919           0 : }
     920             : 
     921             : /*
     922             : */
     923          49 : void Map::addNodeAndUpdate(v3s16 p, MapNode n,
     924             :                 std::map<v3s16, MapBlock*> &modified_blocks,
     925             :                 bool remove_metadata)
     926             : {
     927          49 :         INodeDefManager *ndef = m_gamedef->ndef();
     928             : 
     929             :         /*PrintInfo(m_dout);
     930             :         m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
     931             :                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
     932             : 
     933             :         /*
     934             :                 From this node to nodes underneath:
     935             :                 If lighting is sunlight (1.0), unlight neighbours and
     936             :                 set lighting to 0.
     937             :                 Else discontinue.
     938             :         */
     939             : 
     940          49 :         v3s16 toppos = p + v3s16(0,1,0);
     941             :         //v3s16 bottompos = p + v3s16(0,-1,0);
     942             : 
     943          49 :         bool node_under_sunlight = true;
     944          98 :         std::set<v3s16> light_sources;
     945             : 
     946             :         /*
     947             :                 Collect old node for rollback
     948             :         */
     949          98 :         RollbackNode rollback_oldnode(this, p, m_gamedef);
     950             : 
     951             :         /*
     952             :                 If there is a node at top and it doesn't have sunlight,
     953             :                 there has not been any sunlight going down.
     954             : 
     955             :                 Otherwise there probably is.
     956             :         */
     957             : 
     958             :         bool is_valid_position;
     959          49 :         MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
     960             : 
     961          49 :         if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
     962          37 :                 node_under_sunlight = false;
     963             : 
     964             :         /*
     965             :                 Remove all light that has come out of this node
     966             :         */
     967             : 
     968             :         enum LightBank banks[] =
     969             :         {
     970             :                 LIGHTBANK_DAY,
     971             :                 LIGHTBANK_NIGHT
     972          49 :         };
     973         147 :         for(s32 i=0; i<2; i++)
     974             :         {
     975          98 :                 enum LightBank bank = banks[i];
     976             : 
     977          98 :                 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
     978             : 
     979             :                 // Add the block of the added node to modified_blocks
     980          98 :                 v3s16 blockpos = getNodeBlockPos(p);
     981          98 :                 MapBlock * block = getBlockNoCreate(blockpos);
     982             :                 assert(block != NULL);
     983          98 :                 modified_blocks[blockpos] = block;
     984             : 
     985             :                 assert(isValidPosition(p));
     986             : 
     987             :                 // Unlight neighbours of node.
     988             :                 // This means setting light of all consequent dimmer nodes
     989             :                 // to 0.
     990             :                 // This also collects the nodes at the border which will spread
     991             :                 // light again into this.
     992          98 :                 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
     993             : 
     994          98 :                 n.setLight(bank, 0, ndef);
     995             :         }
     996             : 
     997             :         /*
     998             :                 If node lets sunlight through and is under sunlight, it has
     999             :                 sunlight too.
    1000             :         */
    1001          49 :         if(node_under_sunlight && ndef->get(n).sunlight_propagates)
    1002             :         {
    1003           0 :                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
    1004             :         }
    1005             : 
    1006             :         /*
    1007             :                 Remove node metadata
    1008             :         */
    1009          49 :         if (remove_metadata) {
    1010           7 :                 removeNodeMetadata(p);
    1011             :         }
    1012             : 
    1013             :         /*
    1014             :                 Set the node on the map
    1015             :         */
    1016             : 
    1017          49 :         setNode(p, n);
    1018             : 
    1019             :         /*
    1020             :                 If node is under sunlight and doesn't let sunlight through,
    1021             :                 take all sunlighted nodes under it and clear light from them
    1022             :                 and from where the light has been spread.
    1023             :                 TODO: This could be optimized by mass-unlighting instead
    1024             :                           of looping
    1025             :         */
    1026          49 :         if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
    1027             :         {
    1028          12 :                 s16 y = p.Y - 1;
    1029           0 :                 for(;; y--){
    1030             :                         //m_dout<<DTIME<<"y="<<y<<std::endl;
    1031          12 :                         v3s16 n2pos(p.X, y, p.Z);
    1032             : 
    1033          12 :                         MapNode n2;
    1034             : 
    1035          12 :                         n2 = getNodeNoEx(n2pos, &is_valid_position);
    1036          12 :                         if (!is_valid_position)
    1037          12 :                                 break;
    1038             : 
    1039          12 :                         if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
    1040             :                         {
    1041           0 :                                 unLightNeighbors(LIGHTBANK_DAY,
    1042           0 :                                                 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
    1043           0 :                                                 light_sources, modified_blocks);
    1044           0 :                                 n2.setLight(LIGHTBANK_DAY, 0, ndef);
    1045           0 :                                 setNode(n2pos, n2);
    1046             :                         }
    1047             :                         else
    1048          12 :                                 break;
    1049             :                 }
    1050             :         }
    1051             : 
    1052         147 :         for(s32 i=0; i<2; i++)
    1053             :         {
    1054          98 :                 enum LightBank bank = banks[i];
    1055             : 
    1056             :                 /*
    1057             :                         Spread light from all nodes that might be capable of doing so
    1058             :                 */
    1059          98 :                 spreadLight(bank, light_sources, modified_blocks);
    1060             :         }
    1061             : 
    1062             :         /*
    1063             :                 Update information about whether day and night light differ
    1064             :         */
    1065         389 :         for(std::map<v3s16, MapBlock*>::iterator
    1066          49 :                         i = modified_blocks.begin();
    1067         292 :                         i != modified_blocks.end(); ++i)
    1068             :         {
    1069          97 :                 i->second->expireDayNightDiff();
    1070             :         }
    1071             : 
    1072             :         /*
    1073             :                 Report for rollback
    1074             :         */
    1075          49 :         if(m_gamedef->rollback())
    1076             :         {
    1077           0 :                 RollbackNode rollback_newnode(this, p, m_gamedef);
    1078           0 :                 RollbackAction action;
    1079           0 :                 action.setSetNode(p, rollback_oldnode, rollback_newnode);
    1080           0 :                 m_gamedef->rollback()->reportAction(action);
    1081             :         }
    1082             : 
    1083             :         /*
    1084             :                 Add neighboring liquid nodes and the node itself if it is
    1085             :                 liquid (=water node was added) to transform queue.
    1086             :         */
    1087             :         v3s16 dirs[7] = {
    1088             :                 v3s16(0,0,0), // self
    1089             :                 v3s16(0,0,1), // back
    1090             :                 v3s16(0,1,0), // top
    1091             :                 v3s16(1,0,0), // right
    1092             :                 v3s16(0,0,-1), // front
    1093             :                 v3s16(0,-1,0), // bottom
    1094             :                 v3s16(-1,0,0), // left
    1095          49 :         };
    1096         392 :         for(u16 i=0; i<7; i++)
    1097             :         {
    1098         343 :                 v3s16 p2 = p + dirs[i];
    1099             : 
    1100         343 :                 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
    1101         343 :                 if(is_valid_position
    1102         343 :                                 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
    1103             :                 {
    1104         141 :                         m_transforming_liquid.push_back(p2);
    1105             :                 }
    1106             :         }
    1107          49 : }
    1108             : 
    1109             : /*
    1110             : */
    1111           6 : void Map::removeNodeAndUpdate(v3s16 p,
    1112             :                 std::map<v3s16, MapBlock*> &modified_blocks)
    1113             : {
    1114           6 :         INodeDefManager *ndef = m_gamedef->ndef();
    1115             : 
    1116             :         /*PrintInfo(m_dout);
    1117             :         m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
    1118             :                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
    1119             : 
    1120           6 :         bool node_under_sunlight = true;
    1121             : 
    1122           6 :         v3s16 toppos = p + v3s16(0,1,0);
    1123             : 
    1124             :         // Node will be replaced with this
    1125           6 :         content_t replace_material = CONTENT_AIR;
    1126             : 
    1127             :         /*
    1128             :                 Collect old node for rollback
    1129             :         */
    1130          12 :         RollbackNode rollback_oldnode(this, p, m_gamedef);
    1131             : 
    1132             :         /*
    1133             :                 If there is a node at top and it doesn't have sunlight,
    1134             :                 there will be no sunlight going down.
    1135             :         */
    1136             :         bool is_valid_position;
    1137           6 :         MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
    1138             : 
    1139           6 :         if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
    1140           1 :                 node_under_sunlight = false;
    1141             : 
    1142          12 :         std::set<v3s16> light_sources;
    1143             : 
    1144             :         enum LightBank banks[] =
    1145             :         {
    1146             :                 LIGHTBANK_DAY,
    1147             :                 LIGHTBANK_NIGHT
    1148           6 :         };
    1149          18 :         for(s32 i=0; i<2; i++)
    1150             :         {
    1151          12 :                 enum LightBank bank = banks[i];
    1152             : 
    1153             :                 /*
    1154             :                         Unlight neighbors (in case the node is a light source)
    1155             :                 */
    1156          24 :                 unLightNeighbors(bank, p,
    1157          24 :                                 getNodeNoEx(p).getLight(bank, ndef),
    1158          12 :                                 light_sources, modified_blocks);
    1159             :         }
    1160             : 
    1161             :         /*
    1162             :                 Remove node metadata
    1163             :         */
    1164             : 
    1165           6 :         removeNodeMetadata(p);
    1166             : 
    1167             :         /*
    1168             :                 Remove the node.
    1169             :                 This also clears the lighting.
    1170             :         */
    1171             : 
    1172           6 :         MapNode n(replace_material);
    1173           6 :         setNode(p, n);
    1174             : 
    1175          18 :         for(s32 i=0; i<2; i++)
    1176             :         {
    1177          12 :                 enum LightBank bank = banks[i];
    1178             : 
    1179             :                 /*
    1180             :                         Recalculate lighting
    1181             :                 */
    1182          12 :                 spreadLight(bank, light_sources, modified_blocks);
    1183             :         }
    1184             : 
    1185             :         // Add the block of the removed node to modified_blocks
    1186           6 :         v3s16 blockpos = getNodeBlockPos(p);
    1187           6 :         MapBlock * block = getBlockNoCreate(blockpos);
    1188             :         assert(block != NULL);
    1189           6 :         modified_blocks[blockpos] = block;
    1190             : 
    1191             :         /*
    1192             :                 If the removed node was under sunlight, propagate the
    1193             :                 sunlight down from it and then light all neighbors
    1194             :                 of the propagated blocks.
    1195             :         */
    1196           6 :         if(node_under_sunlight)
    1197             :         {
    1198           5 :                 s16 ybottom = propagateSunlight(p, modified_blocks);
    1199             :                 /*m_dout<<DTIME<<"Node was under sunlight. "
    1200             :                                 "Propagating sunlight";
    1201             :                 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
    1202           5 :                 s16 y = p.Y;
    1203          15 :                 for(; y >= ybottom; y--)
    1204             :                 {
    1205           5 :                         v3s16 p2(p.X, y, p.Z);
    1206             :                         /*m_dout<<DTIME<<"lighting neighbors of node ("
    1207             :                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
    1208             :                                         <<std::endl;*/
    1209           5 :                         lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
    1210             :                 }
    1211             :         }
    1212             :         else
    1213             :         {
    1214             :                 // Set the lighting of this node to 0
    1215             :                 // TODO: Is this needed? Lighting is cleared up there already.
    1216           1 :                 MapNode n = getNodeNoEx(p, &is_valid_position);
    1217           1 :                 if (is_valid_position) {
    1218           1 :                         n.setLight(LIGHTBANK_DAY, 0, ndef);
    1219           1 :                         setNode(p, n);
    1220             :                 } else {
    1221           0 :                         FATAL_ERROR("Invalid position");
    1222             :                 }
    1223             :         }
    1224             : 
    1225          18 :         for(s32 i=0; i<2; i++)
    1226             :         {
    1227          12 :                 enum LightBank bank = banks[i];
    1228             : 
    1229             :                 // Get the brightest neighbour node and propagate light from it
    1230          12 :                 v3s16 n2p = getBrightestNeighbour(bank, p);
    1231             :                 try{
    1232             :                         //MapNode n2 = getNode(n2p);
    1233          12 :                         lightNeighbors(bank, n2p, modified_blocks);
    1234             :                 }
    1235           0 :                 catch(InvalidPositionException &e)
    1236             :                 {
    1237             :                 }
    1238             :         }
    1239             : 
    1240             :         /*
    1241             :                 Update information about whether day and night light differ
    1242             :         */
    1243          45 :         for(std::map<v3s16, MapBlock*>::iterator
    1244           6 :                         i = modified_blocks.begin();
    1245          34 :                         i != modified_blocks.end(); ++i)
    1246             :         {
    1247          11 :                 i->second->expireDayNightDiff();
    1248             :         }
    1249             : 
    1250             :         /*
    1251             :                 Report for rollback
    1252             :         */
    1253           6 :         if(m_gamedef->rollback())
    1254             :         {
    1255           0 :                 RollbackNode rollback_newnode(this, p, m_gamedef);
    1256           0 :                 RollbackAction action;
    1257           0 :                 action.setSetNode(p, rollback_oldnode, rollback_newnode);
    1258           0 :                 m_gamedef->rollback()->reportAction(action);
    1259             :         }
    1260             : 
    1261             :         /*
    1262             :                 Add neighboring liquid nodes and this node to transform queue.
    1263             :                 (it's vital for the node itself to get updated last.)
    1264             :         */
    1265             :         v3s16 dirs[7] = {
    1266             :                 v3s16(0,0,1), // back
    1267             :                 v3s16(0,1,0), // top
    1268             :                 v3s16(1,0,0), // right
    1269             :                 v3s16(0,0,-1), // front
    1270             :                 v3s16(0,-1,0), // bottom
    1271             :                 v3s16(-1,0,0), // left
    1272             :                 v3s16(0,0,0), // self
    1273           6 :         };
    1274          48 :         for(u16 i=0; i<7; i++)
    1275             :         {
    1276          42 :                 v3s16 p2 = p + dirs[i];
    1277             : 
    1278             :                 bool is_position_valid;
    1279          42 :                 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
    1280          42 :                 if (is_position_valid
    1281          42 :                                 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
    1282             :                 {
    1283          32 :                         m_transforming_liquid.push_back(p2);
    1284             :                 }
    1285             :         }
    1286           6 : }
    1287             : 
    1288           0 : bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
    1289             : {
    1290           0 :         MapEditEvent event;
    1291           0 :         event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
    1292           0 :         event.p = p;
    1293           0 :         event.n = n;
    1294             : 
    1295           0 :         bool succeeded = true;
    1296             :         try{
    1297           0 :                 std::map<v3s16, MapBlock*> modified_blocks;
    1298           0 :                 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
    1299             : 
    1300             :                 // Copy modified_blocks to event
    1301           0 :                 for(std::map<v3s16, MapBlock*>::iterator
    1302           0 :                                 i = modified_blocks.begin();
    1303           0 :                                 i != modified_blocks.end(); ++i)
    1304             :                 {
    1305           0 :                         event.modified_blocks.insert(i->first);
    1306             :                 }
    1307             :         }
    1308           0 :         catch(InvalidPositionException &e){
    1309           0 :                 succeeded = false;
    1310             :         }
    1311             : 
    1312           0 :         dispatchEvent(&event);
    1313             : 
    1314           0 :         return succeeded;
    1315             : }
    1316             : 
    1317           0 : bool Map::removeNodeWithEvent(v3s16 p)
    1318             : {
    1319           0 :         MapEditEvent event;
    1320           0 :         event.type = MEET_REMOVENODE;
    1321           0 :         event.p = p;
    1322             : 
    1323           0 :         bool succeeded = true;
    1324             :         try{
    1325           0 :                 std::map<v3s16, MapBlock*> modified_blocks;
    1326           0 :                 removeNodeAndUpdate(p, modified_blocks);
    1327             : 
    1328             :                 // Copy modified_blocks to event
    1329           0 :                 for(std::map<v3s16, MapBlock*>::iterator
    1330           0 :                                 i = modified_blocks.begin();
    1331           0 :                                 i != modified_blocks.end(); ++i)
    1332             :                 {
    1333           0 :                         event.modified_blocks.insert(i->first);
    1334             :                 }
    1335             :         }
    1336           0 :         catch(InvalidPositionException &e){
    1337           0 :                 succeeded = false;
    1338             :         }
    1339             : 
    1340           0 :         dispatchEvent(&event);
    1341             : 
    1342           0 :         return succeeded;
    1343             : }
    1344             : 
    1345           0 : bool Map::getDayNightDiff(v3s16 blockpos)
    1346             : {
    1347             :         try{
    1348           0 :                 v3s16 p = blockpos + v3s16(0,0,0);
    1349           0 :                 MapBlock *b = getBlockNoCreate(p);
    1350           0 :                 if(b->getDayNightDiff())
    1351           0 :                         return true;
    1352             :         }
    1353           0 :         catch(InvalidPositionException &e){}
    1354             :         // Leading edges
    1355             :         try{
    1356           0 :                 v3s16 p = blockpos + v3s16(-1,0,0);
    1357           0 :                 MapBlock *b = getBlockNoCreate(p);
    1358           0 :                 if(b->getDayNightDiff())
    1359           0 :                         return true;
    1360             :         }
    1361           0 :         catch(InvalidPositionException &e){}
    1362             :         try{
    1363           0 :                 v3s16 p = blockpos + v3s16(0,-1,0);
    1364           0 :                 MapBlock *b = getBlockNoCreate(p);
    1365           0 :                 if(b->getDayNightDiff())
    1366           0 :                         return true;
    1367             :         }
    1368           0 :         catch(InvalidPositionException &e){}
    1369             :         try{
    1370           0 :                 v3s16 p = blockpos + v3s16(0,0,-1);
    1371           0 :                 MapBlock *b = getBlockNoCreate(p);
    1372           0 :                 if(b->getDayNightDiff())
    1373           0 :                         return true;
    1374             :         }
    1375           0 :         catch(InvalidPositionException &e){}
    1376             :         // Trailing edges
    1377             :         try{
    1378           0 :                 v3s16 p = blockpos + v3s16(1,0,0);
    1379           0 :                 MapBlock *b = getBlockNoCreate(p);
    1380           0 :                 if(b->getDayNightDiff())
    1381           0 :                         return true;
    1382             :         }
    1383           0 :         catch(InvalidPositionException &e){}
    1384             :         try{
    1385           0 :                 v3s16 p = blockpos + v3s16(0,1,0);
    1386           0 :                 MapBlock *b = getBlockNoCreate(p);
    1387           0 :                 if(b->getDayNightDiff())
    1388           0 :                         return true;
    1389             :         }
    1390           0 :         catch(InvalidPositionException &e){}
    1391             :         try{
    1392           0 :                 v3s16 p = blockpos + v3s16(0,0,1);
    1393           0 :                 MapBlock *b = getBlockNoCreate(p);
    1394           0 :                 if(b->getDayNightDiff())
    1395           0 :                         return true;
    1396             :         }
    1397           0 :         catch(InvalidPositionException &e){}
    1398             : 
    1399           0 :         return false;
    1400             : }
    1401             : 
    1402             : /*
    1403             :         Updates usage timers
    1404             : */
    1405           6 : void Map::timerUpdate(float dtime, float unload_timeout,
    1406             :                 std::vector<v3s16> *unloaded_blocks)
    1407             : {
    1408           6 :         bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
    1409             : 
    1410             :         // Profile modified reasons
    1411          12 :         Profiler modprofiler;
    1412             : 
    1413          12 :         std::vector<v2s16> sector_deletion_queue;
    1414           6 :         u32 deleted_blocks_count = 0;
    1415           6 :         u32 saved_blocks_count = 0;
    1416           6 :         u32 block_count_all = 0;
    1417             : 
    1418           6 :         beginSave();
    1419        2211 :         for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
    1420        1474 :                 si != m_sectors.end(); ++si) {
    1421         731 :                 MapSector *sector = si->second;
    1422             : 
    1423         731 :                 bool all_blocks_deleted = true;
    1424             : 
    1425        1462 :                 MapBlockVect blocks;
    1426         731 :                 sector->getBlocks(blocks);
    1427             : 
    1428        8766 :                 for(MapBlockVect::iterator i = blocks.begin();
    1429        5844 :                                 i != blocks.end(); ++i) {
    1430        2191 :                         MapBlock *block = (*i);
    1431             : 
    1432        2191 :                         block->incrementUsageTimer(dtime);
    1433             : 
    1434        2191 :                         if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
    1435           0 :                                 v3s16 p = block->getPos();
    1436             : 
    1437             :                                 // Save if modified
    1438           0 :                                 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
    1439           0 :                                         modprofiler.add(block->getModifiedReasonString(), 1);
    1440           0 :                                         if (!saveBlock(block))
    1441           0 :                                                 continue;
    1442           0 :                                         saved_blocks_count++;
    1443             :                                 }
    1444             : 
    1445             :                                 // Delete from memory
    1446           0 :                                 sector->deleteBlock(block);
    1447             : 
    1448           0 :                                 if(unloaded_blocks)
    1449           0 :                                         unloaded_blocks->push_back(p);
    1450             : 
    1451           0 :                                 deleted_blocks_count++;
    1452             :                         }
    1453             :                         else {
    1454        2191 :                                 all_blocks_deleted = false;
    1455        2191 :                                 block_count_all++;
    1456             :                         }
    1457             :                 }
    1458             : 
    1459         731 :                 if(all_blocks_deleted) {
    1460           0 :                         sector_deletion_queue.push_back(si->first);
    1461             :                 }
    1462             :         }
    1463           6 :         endSave();
    1464             : 
    1465             :         // Finally delete the empty sectors
    1466           6 :         deleteSectors(sector_deletion_queue);
    1467             : 
    1468           6 :         if(deleted_blocks_count != 0)
    1469             :         {
    1470           0 :                 PrintInfo(infostream); // ServerMap/ClientMap:
    1471           0 :                 infostream<<"Unloaded "<<deleted_blocks_count
    1472           0 :                                 <<" blocks from memory";
    1473           0 :                 if(save_before_unloading)
    1474           0 :                         infostream<<", of which "<<saved_blocks_count<<" were written";
    1475           0 :                 infostream<<", "<<block_count_all<<" blocks in memory";
    1476           0 :                 infostream<<"."<<std::endl;
    1477           0 :                 if(saved_blocks_count != 0){
    1478           0 :                         PrintInfo(infostream); // ServerMap/ClientMap:
    1479           0 :                         infostream<<"Blocks modified by: "<<std::endl;
    1480           0 :                         modprofiler.print(infostream);
    1481             :                 }
    1482             :         }
    1483           6 : }
    1484             : 
    1485           0 : void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
    1486             : {
    1487           0 :         timerUpdate(0.0, -1.0, unloaded_blocks);
    1488           0 : }
    1489             : 
    1490           6 : void Map::deleteSectors(std::vector<v2s16> &sectorList)
    1491             : {
    1492          18 :         for(std::vector<v2s16>::iterator j = sectorList.begin();
    1493          12 :                 j != sectorList.end(); ++j) {
    1494           0 :                 MapSector *sector = m_sectors[*j];
    1495             :                 // If sector is in sector cache, remove it from there
    1496           0 :                 if(m_sector_cache == sector)
    1497           0 :                         m_sector_cache = NULL;
    1498             :                 // Remove from map and delete
    1499           0 :                 m_sectors.erase(*j);
    1500           0 :                 delete sector;
    1501             :         }
    1502           6 : }
    1503             : 
    1504           0 : void Map::PrintInfo(std::ostream &out)
    1505             : {
    1506           0 :         out<<"Map: ";
    1507           0 : }
    1508             : 
    1509             : #define WATER_DROP_BOOST 4
    1510             : 
    1511             : enum NeighborType {
    1512             :         NEIGHBOR_UPPER,
    1513             :         NEIGHBOR_SAME_LEVEL,
    1514             :         NEIGHBOR_LOWER
    1515             : };
    1516           0 : struct NodeNeighbor {
    1517             :         MapNode n;
    1518             :         NeighborType t;
    1519             :         v3s16 p;
    1520             :         bool l; //can liquid
    1521             : 
    1522           0 :         NodeNeighbor()
    1523           0 :                 : n(CONTENT_AIR)
    1524           0 :         { }
    1525             : 
    1526           0 :         NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
    1527             :                 : n(node),
    1528             :                   t(n_type),
    1529           0 :                   p(pos)
    1530           0 :         { }
    1531             : };
    1532             : 
    1533           0 : void Map::transforming_liquid_add(v3s16 p) {
    1534           0 :         m_transforming_liquid.push_back(p);
    1535           0 : }
    1536             : 
    1537           0 : s32 Map::transforming_liquid_size() {
    1538           0 :         return m_transforming_liquid.size();
    1539             : }
    1540             : 
    1541           0 : void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
    1542             : {
    1543             : 
    1544           0 :         INodeDefManager *nodemgr = m_gamedef->ndef();
    1545             : 
    1546           0 :         DSTACK(__FUNCTION_NAME);
    1547             :         //TimeTaker timer("transformLiquids()");
    1548             : 
    1549           0 :         u32 loopcount = 0;
    1550           0 :         u32 initial_size = m_transforming_liquid.size();
    1551             : 
    1552             :         /*if(initial_size != 0)
    1553             :                 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
    1554             : 
    1555             :         // list of nodes that due to viscosity have not reached their max level height
    1556           0 :         std::deque<v3s16> must_reflow;
    1557             : 
    1558             :         // List of MapBlocks that will require a lighting update (due to lava)
    1559           0 :         std::map<v3s16, MapBlock*> lighting_modified_blocks;
    1560             : 
    1561           0 :         u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
    1562           0 :         u32 loop_max = liquid_loop_max;
    1563             : 
    1564             : #if 0
    1565             : 
    1566             :         /* If liquid_loop_max is not keeping up with the queue size increase
    1567             :          * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
    1568             :          */
    1569             :         if (m_transforming_liquid.size() > loop_max * 2) {
    1570             :                 // "Burst" mode
    1571             :                 float server_step = g_settings->getFloat("dedicated_server_step");
    1572             :                 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
    1573             :                         m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
    1574             :         } else {
    1575             :                 m_transforming_liquid_loop_count_multiplier = 1.0;
    1576             :         }
    1577             : 
    1578             :         loop_max *= m_transforming_liquid_loop_count_multiplier;
    1579             : #endif
    1580             : 
    1581           0 :         while(m_transforming_liquid.size() != 0)
    1582             :         {
    1583             :                 // This should be done here so that it is done when continue is used
    1584           0 :                 if(loopcount >= initial_size || loopcount >= loop_max)
    1585             :                         break;
    1586           0 :                 loopcount++;
    1587             : 
    1588             :                 /*
    1589             :                         Get a queued transforming liquid node
    1590             :                 */
    1591           0 :                 v3s16 p0 = m_transforming_liquid.front();
    1592           0 :                 m_transforming_liquid.pop_front();
    1593             : 
    1594           0 :                 MapNode n0 = getNodeNoEx(p0);
    1595             : 
    1596             :                 /*
    1597             :                         Collect information about current node
    1598             :                  */
    1599           0 :                 s8 liquid_level = -1;
    1600           0 :                 content_t liquid_kind = CONTENT_IGNORE;
    1601           0 :                 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
    1602           0 :                 switch (liquid_type) {
    1603             :                         case LIQUID_SOURCE:
    1604           0 :                                 liquid_level = LIQUID_LEVEL_SOURCE;
    1605           0 :                                 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
    1606           0 :                                 break;
    1607             :                         case LIQUID_FLOWING:
    1608           0 :                                 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
    1609           0 :                                 liquid_kind = n0.getContent();
    1610           0 :                                 break;
    1611             :                         case LIQUID_NONE:
    1612             :                                 // if this is an air node, it *could* be transformed into a liquid. otherwise,
    1613             :                                 // continue with the next node.
    1614           0 :                                 if (n0.getContent() != CONTENT_AIR)
    1615           0 :                                         continue;
    1616           0 :                                 liquid_kind = CONTENT_AIR;
    1617           0 :                                 break;
    1618             :                 }
    1619             : 
    1620             :                 /*
    1621             :                         Collect information about the environment
    1622             :                  */
    1623           0 :                 const v3s16 *dirs = g_6dirs;
    1624           0 :                 NodeNeighbor sources[6]; // surrounding sources
    1625           0 :                 int num_sources = 0;
    1626           0 :                 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
    1627           0 :                 int num_flows = 0;
    1628           0 :                 NodeNeighbor airs[6]; // surrounding air
    1629           0 :                 int num_airs = 0;
    1630           0 :                 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
    1631           0 :                 int num_neutrals = 0;
    1632           0 :                 bool flowing_down = false;
    1633           0 :                 for (u16 i = 0; i < 6; i++) {
    1634           0 :                         NeighborType nt = NEIGHBOR_SAME_LEVEL;
    1635           0 :                         switch (i) {
    1636             :                                 case 1:
    1637           0 :                                         nt = NEIGHBOR_UPPER;
    1638           0 :                                         break;
    1639             :                                 case 4:
    1640           0 :                                         nt = NEIGHBOR_LOWER;
    1641           0 :                                         break;
    1642             :                         }
    1643           0 :                         v3s16 npos = p0 + dirs[i];
    1644           0 :                         NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
    1645           0 :                         switch (nodemgr->get(nb.n.getContent()).liquid_type) {
    1646             :                                 case LIQUID_NONE:
    1647           0 :                                         if (nb.n.getContent() == CONTENT_AIR) {
    1648           0 :                                                 airs[num_airs++] = nb;
    1649             :                                                 // if the current node is a water source the neighbor
    1650             :                                                 // should be enqueded for transformation regardless of whether the
    1651             :                                                 // current node changes or not.
    1652           0 :                                                 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
    1653           0 :                                                         m_transforming_liquid.push_back(npos);
    1654             :                                                 // if the current node happens to be a flowing node, it will start to flow down here.
    1655           0 :                                                 if (nb.t == NEIGHBOR_LOWER) {
    1656           0 :                                                         flowing_down = true;
    1657             :                                                 }
    1658             :                                         } else {
    1659           0 :                                                 neutrals[num_neutrals++] = nb;
    1660             :                                         }
    1661           0 :                                         break;
    1662             :                                 case LIQUID_SOURCE:
    1663             :                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
    1664           0 :                                         if (liquid_kind == CONTENT_AIR)
    1665           0 :                                                 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
    1666           0 :                                         if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
    1667           0 :                                                 neutrals[num_neutrals++] = nb;
    1668             :                                         } else {
    1669             :                                                 // Do not count bottom source, it will screw things up
    1670           0 :                                                 if(dirs[i].Y != -1)
    1671           0 :                                                         sources[num_sources++] = nb;
    1672             :                                         }
    1673           0 :                                         break;
    1674             :                                 case LIQUID_FLOWING:
    1675             :                                         // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
    1676           0 :                                         if (liquid_kind == CONTENT_AIR)
    1677           0 :                                                 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
    1678           0 :                                         if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
    1679           0 :                                                 neutrals[num_neutrals++] = nb;
    1680             :                                         } else {
    1681           0 :                                                 flows[num_flows++] = nb;
    1682           0 :                                                 if (nb.t == NEIGHBOR_LOWER)
    1683           0 :                                                         flowing_down = true;
    1684             :                                         }
    1685           0 :                                         break;
    1686             :                         }
    1687             :                 }
    1688             : 
    1689             :                 /*
    1690             :                         decide on the type (and possibly level) of the current node
    1691             :                  */
    1692             :                 content_t new_node_content;
    1693           0 :                 s8 new_node_level = -1;
    1694           0 :                 s8 max_node_level = -1;
    1695             : 
    1696           0 :                 u8 range = nodemgr->get(liquid_kind).liquid_range;
    1697           0 :                 if (range > LIQUID_LEVEL_MAX+1)
    1698           0 :                         range = LIQUID_LEVEL_MAX+1;
    1699             : 
    1700           0 :                 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
    1701             :                         // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
    1702             :                         // or the flowing alternative of the first of the surrounding sources (if it's air), so
    1703             :                         // it's perfectly safe to use liquid_kind here to determine the new node content.
    1704           0 :                         new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
    1705           0 :                 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
    1706             :                         // liquid_kind is set properly, see above
    1707           0 :                         new_node_content = liquid_kind;
    1708           0 :                         max_node_level = new_node_level = LIQUID_LEVEL_MAX;
    1709           0 :                         if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
    1710           0 :                                 new_node_content = CONTENT_AIR;
    1711             :                 } else {
    1712             :                         // no surrounding sources, so get the maximum level that can flow into this node
    1713           0 :                         for (u16 i = 0; i < num_flows; i++) {
    1714           0 :                                 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
    1715           0 :                                 switch (flows[i].t) {
    1716             :                                         case NEIGHBOR_UPPER:
    1717           0 :                                                 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
    1718           0 :                                                         max_node_level = LIQUID_LEVEL_MAX;
    1719           0 :                                                         if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
    1720           0 :                                                                 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
    1721           0 :                                                 } else if (nb_liquid_level > max_node_level)
    1722           0 :                                                         max_node_level = nb_liquid_level;
    1723           0 :                                                 break;
    1724             :                                         case NEIGHBOR_LOWER:
    1725           0 :                                                 break;
    1726             :                                         case NEIGHBOR_SAME_LEVEL:
    1727           0 :                                                 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
    1728           0 :                                                         nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
    1729           0 :                                                         max_node_level = nb_liquid_level - 1;
    1730             :                                                 }
    1731           0 :                                                 break;
    1732             :                                 }
    1733             :                         }
    1734             : 
    1735           0 :                         u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
    1736           0 :                         if (viscosity > 1 && max_node_level != liquid_level) {
    1737             :                                 // amount to gain, limited by viscosity
    1738             :                                 // must be at least 1 in absolute value
    1739           0 :                                 s8 level_inc = max_node_level - liquid_level;
    1740           0 :                                 if (level_inc < -viscosity || level_inc > viscosity)
    1741           0 :                                         new_node_level = liquid_level + level_inc/viscosity;
    1742           0 :                                 else if (level_inc < 0)
    1743           0 :                                         new_node_level = liquid_level - 1;
    1744           0 :                                 else if (level_inc > 0)
    1745           0 :                                         new_node_level = liquid_level + 1;
    1746           0 :                                 if (new_node_level != max_node_level)
    1747           0 :                                         must_reflow.push_back(p0);
    1748             :                         } else
    1749           0 :                                 new_node_level = max_node_level;
    1750             : 
    1751           0 :                         if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
    1752           0 :                                 new_node_content = liquid_kind;
    1753             :                         else
    1754           0 :                                 new_node_content = CONTENT_AIR;
    1755             : 
    1756             :                 }
    1757             : 
    1758             :                 /*
    1759             :                         check if anything has changed. if not, just continue with the next node.
    1760             :                  */
    1761           0 :                 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
    1762           0 :                                                                                  ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
    1763           0 :                                                                                  ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
    1764           0 :                                                                                  == flowing_down)))
    1765           0 :                         continue;
    1766             : 
    1767             : 
    1768             :                 /*
    1769             :                         update the current node
    1770             :                  */
    1771           0 :                 MapNode n00 = n0;
    1772             :                 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
    1773           0 :                 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
    1774             :                         // set level to last 3 bits, flowing down bit to 4th bit
    1775           0 :                         n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
    1776             :                 } else {
    1777             :                         // set the liquid level and flow bit to 0
    1778           0 :                         n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
    1779             :                 }
    1780           0 :                 n0.setContent(new_node_content);
    1781             : 
    1782             :                 // Find out whether there is a suspect for this action
    1783           0 :                 std::string suspect;
    1784           0 :                 if(m_gamedef->rollback()) {
    1785           0 :                         suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
    1786             :                 }
    1787             : 
    1788           0 :                 if(m_gamedef->rollback() && !suspect.empty()){
    1789             :                         // Blame suspect
    1790           0 :                         RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
    1791             :                         // Get old node for rollback
    1792           0 :                         RollbackNode rollback_oldnode(this, p0, m_gamedef);
    1793             :                         // Set node
    1794           0 :                         setNode(p0, n0);
    1795             :                         // Report
    1796           0 :                         RollbackNode rollback_newnode(this, p0, m_gamedef);
    1797           0 :                         RollbackAction action;
    1798           0 :                         action.setSetNode(p0, rollback_oldnode, rollback_newnode);
    1799           0 :                         m_gamedef->rollback()->reportAction(action);
    1800             :                 } else {
    1801             :                         // Set node
    1802           0 :                         setNode(p0, n0);
    1803             :                 }
    1804             : 
    1805           0 :                 v3s16 blockpos = getNodeBlockPos(p0);
    1806           0 :                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1807           0 :                 if(block != NULL) {
    1808           0 :                         modified_blocks[blockpos] =  block;
    1809             :                         // If new or old node emits light, MapBlock requires lighting update
    1810           0 :                         if(nodemgr->get(n0).light_source != 0 ||
    1811           0 :                                         nodemgr->get(n00).light_source != 0)
    1812           0 :                                 lighting_modified_blocks[block->getPos()] = block;
    1813             :                 }
    1814             : 
    1815             :                 /*
    1816             :                         enqueue neighbors for update if neccessary
    1817             :                  */
    1818           0 :                 switch (nodemgr->get(n0.getContent()).liquid_type) {
    1819             :                         case LIQUID_SOURCE:
    1820             :                         case LIQUID_FLOWING:
    1821             :                                 // make sure source flows into all neighboring nodes
    1822           0 :                                 for (u16 i = 0; i < num_flows; i++)
    1823           0 :                                         if (flows[i].t != NEIGHBOR_UPPER)
    1824           0 :                                                 m_transforming_liquid.push_back(flows[i].p);
    1825           0 :                                 for (u16 i = 0; i < num_airs; i++)
    1826           0 :                                         if (airs[i].t != NEIGHBOR_UPPER)
    1827           0 :                                                 m_transforming_liquid.push_back(airs[i].p);
    1828           0 :                                 break;
    1829             :                         case LIQUID_NONE:
    1830             :                                 // this flow has turned to air; neighboring flows might need to do the same
    1831           0 :                                 for (u16 i = 0; i < num_flows; i++)
    1832           0 :                                         m_transforming_liquid.push_back(flows[i].p);
    1833           0 :                                 break;
    1834             :                 }
    1835             :         }
    1836             :         //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
    1837             : 
    1838           0 :         for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
    1839           0 :                 m_transforming_liquid.push_back(*iter);
    1840             : 
    1841           0 :         updateLighting(lighting_modified_blocks, modified_blocks);
    1842             : 
    1843             : 
    1844             :         /* ----------------------------------------------------------------------
    1845             :          * Manage the queue so that it does not grow indefinately
    1846             :          */
    1847           0 :         u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
    1848             : 
    1849           0 :         if (time_until_purge == 0)
    1850           0 :                 return; // Feature disabled
    1851             : 
    1852           0 :         time_until_purge *= 1000;       // seconds -> milliseconds
    1853             : 
    1854           0 :         u32 curr_time = getTime(PRECISION_MILLI);
    1855           0 :         u32 prev_unprocessed = m_unprocessed_count;
    1856           0 :         m_unprocessed_count = m_transforming_liquid.size();
    1857             : 
    1858             :         // if unprocessed block count is decreasing or stable
    1859           0 :         if (m_unprocessed_count <= prev_unprocessed) {
    1860           0 :                 m_queue_size_timer_started = false;
    1861             :         } else {
    1862           0 :                 if (!m_queue_size_timer_started)
    1863           0 :                         m_inc_trending_up_start_time = curr_time;
    1864           0 :                 m_queue_size_timer_started = true;
    1865             :         }
    1866             : 
    1867             :         // Account for curr_time overflowing
    1868           0 :         if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
    1869           0 :                 m_queue_size_timer_started = false;
    1870             : 
    1871             :         /* If the queue has been growing for more than liquid_queue_purge_time seconds
    1872             :          * and the number of unprocessed blocks is still > liquid_loop_max then we
    1873             :          * cannot keep up; dump the oldest blocks from the queue so that the queue
    1874             :          * has liquid_loop_max items in it
    1875             :          */
    1876           0 :         if (m_queue_size_timer_started
    1877           0 :                         && curr_time - m_inc_trending_up_start_time > time_until_purge
    1878           0 :                         && m_unprocessed_count > liquid_loop_max) {
    1879             : 
    1880           0 :                 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
    1881             : 
    1882           0 :                 infostream << "transformLiquids(): DUMPING " << dump_qty
    1883           0 :                            << " blocks from the queue" << std::endl;
    1884             : 
    1885           0 :                 while (dump_qty--)
    1886           0 :                         m_transforming_liquid.pop_front();
    1887             : 
    1888           0 :                 m_queue_size_timer_started = false; // optimistically assume we can keep up now
    1889           0 :                 m_unprocessed_count = m_transforming_liquid.size();
    1890             :         }
    1891             : }
    1892             : 
    1893           0 : std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
    1894             : {
    1895           0 :         std::vector<v3s16> positions_with_meta;
    1896             : 
    1897           0 :         sortBoxVerticies(p1, p2);
    1898           0 :         v3s16 bpmin = getNodeBlockPos(p1);
    1899           0 :         v3s16 bpmax = getNodeBlockPos(p2);
    1900             : 
    1901           0 :         VoxelArea area(p1, p2);
    1902             : 
    1903           0 :         for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
    1904           0 :         for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
    1905           0 :         for (s16 x = bpmin.X; x <= bpmax.X; x++) {
    1906           0 :                 v3s16 blockpos(x, y, z);
    1907             : 
    1908           0 :                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1909           0 :                 if (!block) {
    1910           0 :                         verbosestream << "Map::getNodeMetadata(): Need to emerge "
    1911           0 :                                 << PP(blockpos) << std::endl;
    1912           0 :                         block = emergeBlock(blockpos, false);
    1913             :                 }
    1914           0 :                 if (!block) {
    1915           0 :                         infostream << "WARNING: Map::getNodeMetadata(): Block not found"
    1916           0 :                                 << std::endl;
    1917           0 :                         continue;
    1918             :                 }
    1919             : 
    1920           0 :                 v3s16 p_base = blockpos * MAP_BLOCKSIZE;
    1921           0 :                 std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
    1922           0 :                 for (size_t i = 0; i != keys.size(); i++) {
    1923           0 :                         v3s16 p(keys[i] + p_base);
    1924           0 :                         if (!area.contains(p))
    1925           0 :                                 continue;
    1926             : 
    1927           0 :                         positions_with_meta.push_back(p);
    1928             :                 }
    1929             :         }
    1930             : 
    1931           0 :         return positions_with_meta;
    1932             : }
    1933             : 
    1934         340 : NodeMetadata *Map::getNodeMetadata(v3s16 p)
    1935             : {
    1936         340 :         v3s16 blockpos = getNodeBlockPos(p);
    1937         340 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    1938         340 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1939         340 :         if(!block){
    1940           0 :                 infostream<<"Map::getNodeMetadata(): Need to emerge "
    1941           0 :                                 <<PP(blockpos)<<std::endl;
    1942           0 :                 block = emergeBlock(blockpos, false);
    1943             :         }
    1944         340 :         if(!block){
    1945           0 :                 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
    1946           0 :                                 <<std::endl;
    1947           0 :                 return NULL;
    1948             :         }
    1949         340 :         NodeMetadata *meta = block->m_node_metadata.get(p_rel);
    1950         340 :         return meta;
    1951             : }
    1952             : 
    1953           0 : bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
    1954             : {
    1955           0 :         v3s16 blockpos = getNodeBlockPos(p);
    1956           0 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    1957           0 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1958           0 :         if(!block){
    1959           0 :                 infostream<<"Map::setNodeMetadata(): Need to emerge "
    1960           0 :                                 <<PP(blockpos)<<std::endl;
    1961           0 :                 block = emergeBlock(blockpos, false);
    1962             :         }
    1963           0 :         if(!block){
    1964           0 :                 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
    1965           0 :                                 <<std::endl;
    1966           0 :                 return false;
    1967             :         }
    1968           0 :         block->m_node_metadata.set(p_rel, meta);
    1969           0 :         return true;
    1970             : }
    1971             : 
    1972          13 : void Map::removeNodeMetadata(v3s16 p)
    1973             : {
    1974          13 :         v3s16 blockpos = getNodeBlockPos(p);
    1975          13 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    1976          13 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1977          13 :         if(block == NULL)
    1978             :         {
    1979           0 :                 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
    1980           0 :                                 <<std::endl;
    1981           0 :                 return;
    1982             :         }
    1983          13 :         block->m_node_metadata.remove(p_rel);
    1984             : }
    1985             : 
    1986           0 : NodeTimer Map::getNodeTimer(v3s16 p)
    1987             : {
    1988           0 :         v3s16 blockpos = getNodeBlockPos(p);
    1989           0 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    1990           0 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    1991           0 :         if(!block){
    1992           0 :                 infostream<<"Map::getNodeTimer(): Need to emerge "
    1993           0 :                                 <<PP(blockpos)<<std::endl;
    1994           0 :                 block = emergeBlock(blockpos, false);
    1995             :         }
    1996           0 :         if(!block){
    1997           0 :                 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
    1998           0 :                                 <<std::endl;
    1999           0 :                 return NodeTimer();
    2000             :         }
    2001           0 :         NodeTimer t = block->m_node_timers.get(p_rel);
    2002           0 :         return t;
    2003             : }
    2004             : 
    2005           0 : void Map::setNodeTimer(v3s16 p, NodeTimer t)
    2006             : {
    2007           0 :         v3s16 blockpos = getNodeBlockPos(p);
    2008           0 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    2009           0 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    2010           0 :         if(!block){
    2011           0 :                 infostream<<"Map::setNodeTimer(): Need to emerge "
    2012           0 :                                 <<PP(blockpos)<<std::endl;
    2013           0 :                 block = emergeBlock(blockpos, false);
    2014             :         }
    2015           0 :         if(!block){
    2016           0 :                 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
    2017           0 :                                 <<std::endl;
    2018           0 :                 return;
    2019             :         }
    2020           0 :         block->m_node_timers.set(p_rel, t);
    2021             : }
    2022             : 
    2023           0 : void Map::removeNodeTimer(v3s16 p)
    2024             : {
    2025           0 :         v3s16 blockpos = getNodeBlockPos(p);
    2026           0 :         v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
    2027           0 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    2028           0 :         if(block == NULL)
    2029             :         {
    2030           0 :                 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
    2031           0 :                                 <<std::endl;
    2032           0 :                 return;
    2033             :         }
    2034           0 :         block->m_node_timers.remove(p_rel);
    2035             : }
    2036             : 
    2037             : /*
    2038             :         ServerMap
    2039             : */
    2040           0 : ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
    2041             :         Map(dout_server, gamedef),
    2042             :         m_emerge(emerge),
    2043           0 :         m_map_metadata_changed(true)
    2044             : {
    2045           0 :         verbosestream<<__FUNCTION_NAME<<std::endl;
    2046             : 
    2047             :         /*
    2048             :                 Try to load map; if not found, create a new one.
    2049             :         */
    2050             : 
    2051             :         // Determine which database backend to use
    2052           0 :         std::string conf_path = savedir + DIR_DELIM + "world.mt";
    2053           0 :         Settings conf;
    2054           0 :         bool succeeded = conf.readConfigFile(conf_path.c_str());
    2055           0 :         if (!succeeded || !conf.exists("backend")) {
    2056             :                 // fall back to sqlite3
    2057           0 :                 conf.set("backend", "sqlite3");
    2058             :         }
    2059           0 :         std::string backend = conf.get("backend");
    2060           0 :         dbase = createDatabase(backend, savedir, conf);
    2061             : 
    2062           0 :         if (!conf.updateConfigFile(conf_path.c_str()))
    2063           0 :                 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
    2064             : 
    2065           0 :         m_savedir = savedir;
    2066           0 :         m_map_saving_enabled = false;
    2067             : 
    2068             :         try
    2069             :         {
    2070             :                 // If directory exists, check contents and load if possible
    2071           0 :                 if(fs::PathExists(m_savedir))
    2072             :                 {
    2073             :                         // If directory is empty, it is safe to save into it.
    2074           0 :                         if(fs::GetDirListing(m_savedir).size() == 0)
    2075             :                         {
    2076           0 :                                 infostream<<"ServerMap: Empty save directory is valid."
    2077           0 :                                                 <<std::endl;
    2078           0 :                                 m_map_saving_enabled = true;
    2079             :                         }
    2080             :                         else
    2081             :                         {
    2082             :                                 try{
    2083             :                                         // Load map metadata (seed, chunksize)
    2084           0 :                                         loadMapMeta();
    2085             :                                 }
    2086           0 :                                 catch(SettingNotFoundException &e){
    2087           0 :                                         infostream<<"ServerMap:  Some metadata not found."
    2088           0 :                                                           <<" Using default settings."<<std::endl;
    2089             :                                 }
    2090           0 :                                 catch(FileNotGoodException &e){
    2091           0 :                                         infostream<<"WARNING: Could not load map metadata"
    2092             :                                                         //<<" Disabling chunk-based generator."
    2093           0 :                                                         <<std::endl;
    2094             :                                         //m_chunksize = 0;
    2095             :                                 }
    2096             : 
    2097           0 :                                 infostream<<"ServerMap: Successfully loaded map "
    2098           0 :                                                 <<"metadata from "<<savedir
    2099           0 :                                                 <<", assuming valid save directory."
    2100           0 :                                                 <<" seed="<< m_emerge->params.seed <<"."
    2101           0 :                                                 <<std::endl;
    2102             : 
    2103           0 :                                 m_map_saving_enabled = true;
    2104             :                                 // Map loaded, not creating new one
    2105           0 :                                 return;
    2106             :                         }
    2107             :                 }
    2108             :                 // If directory doesn't exist, it is safe to save to it
    2109             :                 else{
    2110           0 :                         m_map_saving_enabled = true;
    2111             :                 }
    2112             :         }
    2113           0 :         catch(std::exception &e)
    2114             :         {
    2115           0 :                 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
    2116           0 :                                 <<", exception: "<<e.what()<<std::endl;
    2117           0 :                 infostream<<"Please remove the map or fix it."<<std::endl;
    2118           0 :                 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
    2119             :         }
    2120             : 
    2121           0 :         infostream<<"Initializing new map."<<std::endl;
    2122             : 
    2123             :         // Create zero sector
    2124           0 :         emergeSector(v2s16(0,0));
    2125             : 
    2126             :         // Initially write whole map
    2127           0 :         save(MOD_STATE_CLEAN);
    2128             : }
    2129             : 
    2130           0 : ServerMap::~ServerMap()
    2131             : {
    2132           0 :         verbosestream<<__FUNCTION_NAME<<std::endl;
    2133             : 
    2134             :         try
    2135             :         {
    2136           0 :                 if(m_map_saving_enabled)
    2137             :                 {
    2138             :                         // Save only changed parts
    2139           0 :                         save(MOD_STATE_WRITE_AT_UNLOAD);
    2140           0 :                         infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
    2141             :                 }
    2142             :                 else
    2143             :                 {
    2144           0 :                         infostream<<"ServerMap: Map not saved"<<std::endl;
    2145             :                 }
    2146             :         }
    2147           0 :         catch(std::exception &e)
    2148             :         {
    2149           0 :                 infostream<<"ServerMap: Failed to save map to "<<m_savedir
    2150           0 :                                 <<", exception: "<<e.what()<<std::endl;
    2151             :         }
    2152             : 
    2153             :         /*
    2154             :                 Close database if it was opened
    2155             :         */
    2156           0 :         delete dbase;
    2157             : 
    2158             : #if 0
    2159             :         /*
    2160             :                 Free all MapChunks
    2161             :         */
    2162             :         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
    2163             :         for(; i.atEnd() == false; i++)
    2164             :         {
    2165             :                 MapChunk *chunk = i.getNode()->getValue();
    2166             :                 delete chunk;
    2167             :         }
    2168             : #endif
    2169           0 : }
    2170             : 
    2171           0 : u64 ServerMap::getSeed()
    2172             : {
    2173           0 :         return m_emerge->params.seed;
    2174             : }
    2175             : 
    2176           0 : s16 ServerMap::getWaterLevel()
    2177             : {
    2178           0 :         return m_emerge->params.water_level;
    2179             : }
    2180             : 
    2181           0 : bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
    2182             : {
    2183           0 :         bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
    2184           0 :         EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
    2185             : 
    2186           0 :         s16 chunksize = m_emerge->params.chunksize;
    2187           0 :         s16 coffset = -chunksize / 2;
    2188           0 :         v3s16 chunk_offset(coffset, coffset, coffset);
    2189           0 :         v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
    2190           0 :         v3s16 blockpos_min = blockpos_div * chunksize;
    2191           0 :         v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
    2192           0 :         blockpos_min += chunk_offset;
    2193           0 :         blockpos_max += chunk_offset;
    2194             : 
    2195           0 :         v3s16 extra_borders(1,1,1);
    2196             : 
    2197             :         // Do nothing if not inside limits (+-1 because of neighbors)
    2198           0 :         if(blockpos_over_limit(blockpos_min - extra_borders) ||
    2199           0 :                 blockpos_over_limit(blockpos_max + extra_borders))
    2200           0 :                 return false;
    2201             : 
    2202           0 :         data->seed = m_emerge->params.seed;
    2203           0 :         data->blockpos_min = blockpos_min;
    2204           0 :         data->blockpos_max = blockpos_max;
    2205           0 :         data->blockpos_requested = blockpos;
    2206           0 :         data->nodedef = m_gamedef->ndef();
    2207             : 
    2208             :         /*
    2209             :                 Create the whole area of this and the neighboring blocks
    2210             :         */
    2211             :         {
    2212             :                 //TimeTaker timer("initBlockMake() create area");
    2213             : 
    2214           0 :                 for(s16 x=blockpos_min.X-extra_borders.X;
    2215           0 :                                 x<=blockpos_max.X+extra_borders.X; x++)
    2216           0 :                 for(s16 z=blockpos_min.Z-extra_borders.Z;
    2217           0 :                                 z<=blockpos_max.Z+extra_borders.Z; z++)
    2218             :                 {
    2219           0 :                         v2s16 sectorpos(x, z);
    2220             :                         // Sector metadata is loaded from disk if not already loaded.
    2221           0 :                         ServerMapSector *sector = createSector(sectorpos);
    2222           0 :                         FATAL_ERROR_IF(sector == NULL, "createSector() failed");
    2223             :                         (void) sector;
    2224             : 
    2225           0 :                         for(s16 y=blockpos_min.Y-extra_borders.Y;
    2226           0 :                                         y<=blockpos_max.Y+extra_borders.Y; y++)
    2227             :                         {
    2228           0 :                                 v3s16 p(x,y,z);
    2229             :                                 //MapBlock *block = createBlock(p);
    2230             :                                 // 1) get from memory, 2) load from disk
    2231           0 :                                 MapBlock *block = emergeBlock(p, false);
    2232             :                                 // 3) create a blank one
    2233           0 :                                 if(block == NULL)
    2234             :                                 {
    2235           0 :                                         block = createBlock(p);
    2236             : 
    2237             :                                         /*
    2238             :                                                 Block gets sunlight if this is true.
    2239             : 
    2240             :                                                 Refer to the map generator heuristics.
    2241             :                                         */
    2242           0 :                                         bool ug = m_emerge->isBlockUnderground(p);
    2243           0 :                                         block->setIsUnderground(ug);
    2244             :                                 }
    2245             : 
    2246             :                                 // Lighting will not be valid after make_chunk is called
    2247           0 :                                 block->setLightingExpired(true);
    2248             :                                 // Lighting will be calculated
    2249             :                                 //block->setLightingExpired(false);
    2250             :                         }
    2251             :                 }
    2252             :         }
    2253             : 
    2254             :         /*
    2255             :                 Now we have a big empty area.
    2256             : 
    2257             :                 Make a ManualMapVoxelManipulator that contains this and the
    2258             :                 neighboring blocks
    2259             :         */
    2260             : 
    2261             :         // The area that contains this block and it's neighbors
    2262           0 :         v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
    2263           0 :         v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
    2264             : 
    2265           0 :         data->vmanip = new MMVManip(this);
    2266             :         //data->vmanip->setMap(this);
    2267             : 
    2268             :         // Add the area
    2269             :         {
    2270             :                 //TimeTaker timer("initBlockMake() initialEmerge");
    2271           0 :                 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
    2272             :         }
    2273             : 
    2274             :         // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
    2275             : /*      for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
    2276             :                 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
    2277             :                         for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
    2278             :                                 core::map<v3s16, u8>::Node *n;
    2279             :                                 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
    2280             :                                 if (n == NULL)
    2281             :                                         continue;
    2282             :                                 u8 flags = n->getValue();
    2283             :                                 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
    2284             :                                 n->setValue(flags);
    2285             :                         }
    2286             :                 }
    2287             :         }*/
    2288             : 
    2289             :         // Data is ready now.
    2290           0 :         return true;
    2291             : }
    2292             : 
    2293           0 : void ServerMap::finishBlockMake(BlockMakeData *data,
    2294             :                 std::map<v3s16, MapBlock*> &changed_blocks)
    2295             : {
    2296           0 :         v3s16 blockpos_min = data->blockpos_min;
    2297           0 :         v3s16 blockpos_max = data->blockpos_max;
    2298           0 :         v3s16 blockpos_requested = data->blockpos_requested;
    2299             :         /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
    2300             :                         <<blockpos_requested.Y<<","
    2301             :                         <<blockpos_requested.Z<<")"<<std::endl;*/
    2302             : 
    2303           0 :         v3s16 extra_borders(1,1,1);
    2304             : 
    2305           0 :         bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
    2306             : 
    2307             :         /*infostream<<"Resulting vmanip:"<<std::endl;
    2308             :         data->vmanip.print(infostream);*/
    2309             : 
    2310             :         // Make sure affected blocks are loaded
    2311           0 :         for(s16 x=blockpos_min.X-extra_borders.X;
    2312           0 :                         x<=blockpos_max.X+extra_borders.X; x++)
    2313           0 :         for(s16 z=blockpos_min.Z-extra_borders.Z;
    2314           0 :                         z<=blockpos_max.Z+extra_borders.Z; z++)
    2315           0 :         for(s16 y=blockpos_min.Y-extra_borders.Y;
    2316           0 :                         y<=blockpos_max.Y+extra_borders.Y; y++)
    2317             :         {
    2318           0 :                 v3s16 p(x, y, z);
    2319             :                 // Load from disk if not already in memory
    2320           0 :                 emergeBlock(p, false);
    2321             :         }
    2322             : 
    2323             :         /*
    2324             :                 Blit generated stuff to map
    2325             :                 NOTE: blitBackAll adds nearly everything to changed_blocks
    2326             :         */
    2327             :         {
    2328             :                 // 70ms @cs=8
    2329             :                 //TimeTaker timer("finishBlockMake() blitBackAll");
    2330           0 :                 data->vmanip->blitBackAll(&changed_blocks);
    2331             :         }
    2332             : 
    2333           0 :         EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
    2334             : 
    2335             :         /*
    2336             :                 Copy transforming liquid information
    2337             :         */
    2338           0 :         while(data->transforming_liquid.size() > 0)
    2339             :         {
    2340           0 :                 m_transforming_liquid.push_back(data->transforming_liquid.front());
    2341           0 :                 data->transforming_liquid.pop_front();
    2342             :         }
    2343             : 
    2344             :         /*
    2345             :                 Do stuff in central blocks
    2346             :         */
    2347             : 
    2348             :         /*
    2349             :                 Update lighting
    2350             :         */
    2351             :         {
    2352             : #if 0
    2353             :                 TimeTaker t("finishBlockMake lighting update");
    2354             : 
    2355             :                 core::map<v3s16, MapBlock*> lighting_update_blocks;
    2356             : 
    2357             :                 // Center blocks
    2358             :                 for(s16 x=blockpos_min.X-extra_borders.X;
    2359             :                                 x<=blockpos_max.X+extra_borders.X; x++)
    2360             :                 for(s16 z=blockpos_min.Z-extra_borders.Z;
    2361             :                                 z<=blockpos_max.Z+extra_borders.Z; z++)
    2362             :                 for(s16 y=blockpos_min.Y-extra_borders.Y;
    2363             :                                 y<=blockpos_max.Y+extra_borders.Y; y++)
    2364             :                 {
    2365             :                         v3s16 p(x, y, z);
    2366             :                         MapBlock *block = getBlockNoCreateNoEx(p);
    2367             :                         assert(block);
    2368             :                         lighting_update_blocks.insert(block->getPos(), block);
    2369             :                 }
    2370             : 
    2371             :                 updateLighting(lighting_update_blocks, changed_blocks);
    2372             : #endif
    2373             : 
    2374             :                 /*
    2375             :                         Set lighting to non-expired state in all of them.
    2376             :                         This is cheating, but it is not fast enough if all of them
    2377             :                         would actually be updated.
    2378             :                 */
    2379           0 :                 for(s16 x=blockpos_min.X-extra_borders.X;
    2380           0 :                                 x<=blockpos_max.X+extra_borders.X; x++)
    2381           0 :                 for(s16 z=blockpos_min.Z-extra_borders.Z;
    2382           0 :                                 z<=blockpos_max.Z+extra_borders.Z; z++)
    2383           0 :                 for(s16 y=blockpos_min.Y-extra_borders.Y;
    2384           0 :                                 y<=blockpos_max.Y+extra_borders.Y; y++)
    2385             :                 {
    2386           0 :                         v3s16 p(x, y, z);
    2387           0 :                         MapBlock * block = getBlockNoCreateNoEx(p);
    2388           0 :                         if (block != NULL)
    2389           0 :                                 block->setLightingExpired(false);
    2390             :                 }
    2391             : 
    2392             : #if 0
    2393             :                 if(enable_mapgen_debug_info == false)
    2394             :                         t.stop(true); // Hide output
    2395             : #endif
    2396             :         }
    2397             : 
    2398             :         /*
    2399             :                 Go through changed blocks
    2400             :         */
    2401           0 :         for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
    2402           0 :                         i != changed_blocks.end(); ++i)
    2403             :         {
    2404           0 :                 MapBlock *block = i->second;
    2405           0 :                 if (!block)
    2406           0 :                         continue;
    2407             :                 /*
    2408             :                         Update day/night difference cache of the MapBlocks
    2409             :                 */
    2410           0 :                 block->expireDayNightDiff();
    2411             :                 /*
    2412             :                         Set block as modified
    2413             :                 */
    2414             :                 block->raiseModified(MOD_STATE_WRITE_NEEDED,
    2415           0 :                         MOD_REASON_EXPIRE_DAYNIGHTDIFF);
    2416             :         }
    2417             : 
    2418             :         /*
    2419             :                 Set central blocks as generated
    2420             :         */
    2421           0 :         for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
    2422           0 :         for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
    2423           0 :         for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
    2424             :         {
    2425           0 :                 v3s16 p(x, y, z);
    2426           0 :                 MapBlock *block = getBlockNoCreateNoEx(p);
    2427           0 :                 if (!block)
    2428           0 :                         continue;
    2429           0 :                 block->setGenerated(true);
    2430             :         }
    2431             : 
    2432             :         /*
    2433             :                 Save changed parts of map
    2434             :                 NOTE: Will be saved later.
    2435             :         */
    2436             :         //save(MOD_STATE_WRITE_AT_UNLOAD);
    2437             : 
    2438             :         /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
    2439             :                         <<","<<blockpos_requested.Y<<","
    2440             :                         <<blockpos_requested.Z<<")"<<std::endl;*/
    2441             : 
    2442             : 
    2443             : #if 0
    2444             :         if(enable_mapgen_debug_info)
    2445             :         {
    2446             :                 /*
    2447             :                         Analyze resulting blocks
    2448             :                 */
    2449             :                 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
    2450             :                 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
    2451             :                 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
    2452             :                 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
    2453             :                 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
    2454             :                 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
    2455             :                 {
    2456             :                         v3s16 p = v3s16(x,y,z);
    2457             :                         MapBlock *block = getBlockNoCreateNoEx(p);
    2458             :                         char spos[20];
    2459             :                         snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
    2460             :                         infostream<<"Generated "<<spos<<": "
    2461             :                                         <<analyze_block(block)<<std::endl;
    2462             :                 }
    2463             :         }
    2464             : #endif
    2465             : 
    2466           0 :         getBlockNoCreateNoEx(blockpos_requested);
    2467           0 : }
    2468             : 
    2469           0 : ServerMapSector * ServerMap::createSector(v2s16 p2d)
    2470             : {
    2471           0 :         DSTACKF("%s: p2d=(%d,%d)",
    2472             :                         __FUNCTION_NAME,
    2473             :                         p2d.X, p2d.Y);
    2474             : 
    2475             :         /*
    2476             :                 Check if it exists already in memory
    2477             :         */
    2478           0 :         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
    2479           0 :         if(sector != NULL)
    2480           0 :                 return sector;
    2481             : 
    2482             :         /*
    2483             :                 Try to load it from disk (with blocks)
    2484             :         */
    2485             :         //if(loadSectorFull(p2d) == true)
    2486             : 
    2487             :         /*
    2488             :                 Try to load metadata from disk
    2489             :         */
    2490             : #if 0
    2491             :         if(loadSectorMeta(p2d) == true)
    2492             :         {
    2493             :                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
    2494             :                 if(sector == NULL)
    2495             :                 {
    2496             :                         infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
    2497             :                         throw InvalidPositionException("");
    2498             :                 }
    2499             :                 return sector;
    2500             :         }
    2501             : #endif
    2502             :         /*
    2503             :                 Do not create over-limit
    2504             :         */
    2505           0 :         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2506           0 :         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2507           0 :         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2508           0 :         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
    2509           0 :                 throw InvalidPositionException("createSector(): pos. over limit");
    2510             : 
    2511             :         /*
    2512             :                 Generate blank sector
    2513             :         */
    2514             : 
    2515           0 :         sector = new ServerMapSector(this, p2d, m_gamedef);
    2516             : 
    2517             :         // Sector position on map in nodes
    2518             :         //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
    2519             : 
    2520             :         /*
    2521             :                 Insert to container
    2522             :         */
    2523           0 :         m_sectors[p2d] = sector;
    2524             : 
    2525           0 :         return sector;
    2526             : }
    2527             : 
    2528             : #if 0
    2529             : /*
    2530             :         This is a quick-hand function for calling makeBlock().
    2531             : */
    2532             : MapBlock * ServerMap::generateBlock(
    2533             :                 v3s16 p,
    2534             :                 std::map<v3s16, MapBlock*> &modified_blocks
    2535             : )
    2536             : {
    2537             :         DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
    2538             : 
    2539             :         /*infostream<<"generateBlock(): "
    2540             :                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
    2541             :                         <<std::endl;*/
    2542             : 
    2543             :         bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
    2544             : 
    2545             :         TimeTaker timer("generateBlock");
    2546             : 
    2547             :         //MapBlock *block = original_dummy;
    2548             : 
    2549             :         v2s16 p2d(p.X, p.Z);
    2550             :         v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
    2551             : 
    2552             :         /*
    2553             :                 Do not generate over-limit
    2554             :         */
    2555             :         if(blockpos_over_limit(p))
    2556             :         {
    2557             :                 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
    2558             :                 throw InvalidPositionException("generateBlock(): pos. over limit");
    2559             :         }
    2560             : 
    2561             :         /*
    2562             :                 Create block make data
    2563             :         */
    2564             :         BlockMakeData data;
    2565             :         initBlockMake(&data, p);
    2566             : 
    2567             :         /*
    2568             :                 Generate block
    2569             :         */
    2570             :         {
    2571             :                 TimeTaker t("mapgen::make_block()");
    2572             :                 mapgen->makeChunk(&data);
    2573             :                 //mapgen::make_block(&data);
    2574             : 
    2575             :                 if(enable_mapgen_debug_info == false)
    2576             :                         t.stop(true); // Hide output
    2577             :         }
    2578             : 
    2579             :         /*
    2580             :                 Blit data back on map, update lighting, add mobs and whatever this does
    2581             :         */
    2582             :         finishBlockMake(&data, modified_blocks);
    2583             : 
    2584             :         /*
    2585             :                 Get central block
    2586             :         */
    2587             :         MapBlock *block = getBlockNoCreateNoEx(p);
    2588             : 
    2589             : #if 0
    2590             :         /*
    2591             :                 Check result
    2592             :         */
    2593             :         if(block)
    2594             :         {
    2595             :                 bool erroneus_content = false;
    2596             :                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
    2597             :                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
    2598             :                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
    2599             :                 {
    2600             :                         v3s16 p(x0,y0,z0);
    2601             :                         MapNode n = block->getNode(p);
    2602             :                         if(n.getContent() == CONTENT_IGNORE)
    2603             :                         {
    2604             :                                 infostream<<"CONTENT_IGNORE at "
    2605             :                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
    2606             :                                                 <<std::endl;
    2607             :                                 erroneus_content = true;
    2608             :                                 assert(0);
    2609             :                         }
    2610             :                 }
    2611             :                 if(erroneus_content)
    2612             :                 {
    2613             :                         assert(0);
    2614             :                 }
    2615             :         }
    2616             : #endif
    2617             : 
    2618             : #if 0
    2619             :         /*
    2620             :                 Generate a completely empty block
    2621             :         */
    2622             :         if(block)
    2623             :         {
    2624             :                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
    2625             :                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
    2626             :                 {
    2627             :                         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
    2628             :                         {
    2629             :                                 MapNode n;
    2630             :                                 n.setContent(CONTENT_AIR);
    2631             :                                 block->setNode(v3s16(x0,y0,z0), n);
    2632             :                         }
    2633             :                 }
    2634             :         }
    2635             : #endif
    2636             : 
    2637             :         if(enable_mapgen_debug_info == false)
    2638             :                 timer.stop(true); // Hide output
    2639             : 
    2640             :         return block;
    2641             : }
    2642             : #endif
    2643             : 
    2644           0 : MapBlock * ServerMap::createBlock(v3s16 p)
    2645             : {
    2646           0 :         DSTACKF("%s: p=(%d,%d,%d)",
    2647             :                         __FUNCTION_NAME, p.X, p.Y, p.Z);
    2648             : 
    2649             :         /*
    2650             :                 Do not create over-limit
    2651             :         */
    2652           0 :         if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2653           0 :         || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2654           0 :         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2655           0 :         || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2656           0 :         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
    2657           0 :         || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
    2658           0 :                 throw InvalidPositionException("createBlock(): pos. over limit");
    2659             : 
    2660           0 :         v2s16 p2d(p.X, p.Z);
    2661           0 :         s16 block_y = p.Y;
    2662             :         /*
    2663             :                 This will create or load a sector if not found in memory.
    2664             :                 If block exists on disk, it will be loaded.
    2665             : 
    2666             :                 NOTE: On old save formats, this will be slow, as it generates
    2667             :                       lighting on blocks for them.
    2668             :         */
    2669             :         ServerMapSector *sector;
    2670             :         try {
    2671           0 :                 sector = (ServerMapSector*)createSector(p2d);
    2672             :                 assert(sector->getId() == MAPSECTOR_SERVER);
    2673             :         }
    2674           0 :         catch(InvalidPositionException &e)
    2675             :         {
    2676           0 :                 infostream<<"createBlock: createSector() failed"<<std::endl;
    2677           0 :                 throw e;
    2678             :         }
    2679             :         /*
    2680             :                 NOTE: This should not be done, or at least the exception
    2681             :                 should not be passed on as std::exception, because it
    2682             :                 won't be catched at all.
    2683             :         */
    2684             :         /*catch(std::exception &e)
    2685             :         {
    2686             :                 infostream<<"createBlock: createSector() failed: "
    2687             :                                 <<e.what()<<std::endl;
    2688             :                 throw e;
    2689             :         }*/
    2690             : 
    2691             :         /*
    2692             :                 Try to get a block from the sector
    2693             :         */
    2694             : 
    2695           0 :         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
    2696           0 :         if(block)
    2697             :         {
    2698           0 :                 if(block->isDummy())
    2699           0 :                         block->unDummify();
    2700           0 :                 return block;
    2701             :         }
    2702             :         // Create blank
    2703           0 :         block = sector->createBlankBlock(block_y);
    2704             : 
    2705           0 :         return block;
    2706             : }
    2707             : 
    2708           0 : MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
    2709             : {
    2710           0 :         DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
    2711             :                         __FUNCTION_NAME,
    2712             :                         p.X, p.Y, p.Z, create_blank);
    2713             : 
    2714             :         {
    2715           0 :                 MapBlock *block = getBlockNoCreateNoEx(p);
    2716           0 :                 if(block && block->isDummy() == false)
    2717           0 :                         return block;
    2718             :         }
    2719             : 
    2720             :         {
    2721           0 :                 MapBlock *block = loadBlock(p);
    2722           0 :                 if(block)
    2723           0 :                         return block;
    2724             :         }
    2725             : 
    2726           0 :         if (create_blank) {
    2727           0 :                 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
    2728           0 :                 MapBlock *block = sector->createBlankBlock(p.Y);
    2729             : 
    2730           0 :                 return block;
    2731             :         }
    2732             : 
    2733             : #if 0
    2734             :         if(allow_generate)
    2735             :         {
    2736             :                 std::map<v3s16, MapBlock*> modified_blocks;
    2737             :                 MapBlock *block = generateBlock(p, modified_blocks);
    2738             :                 if(block)
    2739             :                 {
    2740             :                         MapEditEvent event;
    2741             :                         event.type = MEET_OTHER;
    2742             :                         event.p = p;
    2743             : 
    2744             :                         // Copy modified_blocks to event
    2745             :                         for(std::map<v3s16, MapBlock*>::iterator
    2746             :                                         i = modified_blocks.begin();
    2747             :                                         i != modified_blocks.end(); ++i)
    2748             :                         {
    2749             :                                 event.modified_blocks.insert(i->first);
    2750             :                         }
    2751             : 
    2752             :                         // Queue event
    2753             :                         dispatchEvent(&event);
    2754             : 
    2755             :                         return block;
    2756             :                 }
    2757             :         }
    2758             : #endif
    2759             : 
    2760           0 :         return NULL;
    2761             : }
    2762             : 
    2763           0 : MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
    2764             : {
    2765           0 :         MapBlock *block = getBlockNoCreateNoEx(p3d);
    2766           0 :         if (block == NULL)
    2767           0 :                 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
    2768             : 
    2769           0 :         return block;
    2770             : }
    2771             : 
    2772           0 : void ServerMap::prepareBlock(MapBlock *block) {
    2773           0 : }
    2774             : 
    2775             : // N.B.  This requires no synchronization, since data will not be modified unless
    2776             : // the VoxelManipulator being updated belongs to the same thread.
    2777           0 : void ServerMap::updateVManip(v3s16 pos)
    2778             : {
    2779           0 :         Mapgen *mg = m_emerge->getCurrentMapgen();
    2780           0 :         if (!mg)
    2781           0 :                 return;
    2782             : 
    2783           0 :         MMVManip *vm = mg->vm;
    2784           0 :         if (!vm)
    2785           0 :                 return;
    2786             : 
    2787           0 :         if (!vm->m_area.contains(pos))
    2788           0 :                 return;
    2789             : 
    2790           0 :         s32 idx = vm->m_area.index(pos);
    2791           0 :         vm->m_data[idx] = getNodeNoEx(pos);
    2792           0 :         vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
    2793             : 
    2794           0 :         vm->m_is_dirty = true;
    2795             : }
    2796             : 
    2797           0 : s16 ServerMap::findGroundLevel(v2s16 p2d)
    2798             : {
    2799             : #if 0
    2800             :         /*
    2801             :                 Uh, just do something random...
    2802             :         */
    2803             :         // Find existing map from top to down
    2804             :         s16 max=63;
    2805             :         s16 min=-64;
    2806             :         v3s16 p(p2d.X, max, p2d.Y);
    2807             :         for(; p.Y>min; p.Y--)
    2808             :         {
    2809             :                 MapNode n = getNodeNoEx(p);
    2810             :                 if(n.getContent() != CONTENT_IGNORE)
    2811             :                         break;
    2812             :         }
    2813             :         if(p.Y == min)
    2814             :                 goto plan_b;
    2815             :         // If this node is not air, go to plan b
    2816             :         if(getNodeNoEx(p).getContent() != CONTENT_AIR)
    2817             :                 goto plan_b;
    2818             :         // Search existing walkable and return it
    2819             :         for(; p.Y>min; p.Y--)
    2820             :         {
    2821             :                 MapNode n = getNodeNoEx(p);
    2822             :                 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
    2823             :                         return p.Y;
    2824             :         }
    2825             : 
    2826             :         // Move to plan b
    2827             : plan_b:
    2828             : #endif
    2829             : 
    2830             :         /*
    2831             :                 Determine from map generator noise functions
    2832             :         */
    2833             : 
    2834           0 :         s16 level = m_emerge->getGroundLevelAtPoint(p2d);
    2835           0 :         return level;
    2836             : 
    2837             :         //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
    2838             :         //return (s16)level;
    2839             : }
    2840             : 
    2841           0 : bool ServerMap::loadFromFolders() {
    2842           0 :         if (!dbase->initialized() &&
    2843           0 :                         !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
    2844           0 :                 return true;
    2845           0 :         return false;
    2846             : }
    2847             : 
    2848           0 : void ServerMap::createDirs(std::string path)
    2849             : {
    2850           0 :         if(fs::CreateAllDirs(path) == false)
    2851             :         {
    2852           0 :                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
    2853           0 :                                 <<"\""<<path<<"\""<<std::endl;
    2854           0 :                 throw BaseException("ServerMap failed to create directory");
    2855             :         }
    2856           0 : }
    2857             : 
    2858           0 : std::string ServerMap::getSectorDir(v2s16 pos, int layout)
    2859             : {
    2860             :         char cc[9];
    2861           0 :         switch(layout)
    2862             :         {
    2863             :                 case 1:
    2864           0 :                         snprintf(cc, 9, "%.4x%.4x",
    2865           0 :                                 (unsigned int) pos.X & 0xffff,
    2866           0 :                                 (unsigned int) pos.Y & 0xffff);
    2867             : 
    2868           0 :                         return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
    2869             :                 case 2:
    2870           0 :                         snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
    2871           0 :                                 (unsigned int) pos.X & 0xfff,
    2872           0 :                                 (unsigned int) pos.Y & 0xfff);
    2873             : 
    2874           0 :                         return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
    2875             :                 default:
    2876             :                         assert(false);
    2877           0 :                         return "";
    2878             :         }
    2879             : }
    2880             : 
    2881           0 : v2s16 ServerMap::getSectorPos(std::string dirname)
    2882             : {
    2883           0 :         unsigned int x = 0, y = 0;
    2884             :         int r;
    2885           0 :         std::string component;
    2886           0 :         fs::RemoveLastPathComponent(dirname, &component, 1);
    2887           0 :         if(component.size() == 8)
    2888             :         {
    2889             :                 // Old layout
    2890           0 :                 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
    2891             :         }
    2892           0 :         else if(component.size() == 3)
    2893             :         {
    2894             :                 // New layout
    2895           0 :                 fs::RemoveLastPathComponent(dirname, &component, 2);
    2896           0 :                 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
    2897             :                 // Sign-extend the 12 bit values up to 16 bits...
    2898           0 :                 if(x & 0x800) x |= 0xF000;
    2899           0 :                 if(y & 0x800) y |= 0xF000;
    2900             :         }
    2901             :         else
    2902             :         {
    2903           0 :                 r = -1;
    2904             :         }
    2905             : 
    2906           0 :         FATAL_ERROR_IF(r != 2, "getSectorPos()");
    2907           0 :         v2s16 pos((s16)x, (s16)y);
    2908           0 :         return pos;
    2909             : }
    2910             : 
    2911           0 : v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
    2912             : {
    2913           0 :         v2s16 p2d = getSectorPos(sectordir);
    2914             : 
    2915           0 :         if(blockfile.size() != 4){
    2916           0 :                 throw InvalidFilenameException("Invalid block filename");
    2917             :         }
    2918             :         unsigned int y;
    2919           0 :         int r = sscanf(blockfile.c_str(), "%4x", &y);
    2920           0 :         if(r != 1)
    2921           0 :                 throw InvalidFilenameException("Invalid block filename");
    2922           0 :         return v3s16(p2d.X, y, p2d.Y);
    2923             : }
    2924             : 
    2925           0 : std::string ServerMap::getBlockFilename(v3s16 p)
    2926             : {
    2927             :         char cc[5];
    2928           0 :         snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
    2929           0 :         return cc;
    2930             : }
    2931             : 
    2932           0 : void ServerMap::save(ModifiedState save_level)
    2933             : {
    2934           0 :         DSTACK(__FUNCTION_NAME);
    2935           0 :         if(m_map_saving_enabled == false) {
    2936           0 :                 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
    2937           0 :                 return;
    2938             :         }
    2939             : 
    2940           0 :         if(save_level == MOD_STATE_CLEAN)
    2941           0 :                 infostream<<"ServerMap: Saving whole map, this can take time."
    2942           0 :                                 <<std::endl;
    2943             : 
    2944           0 :         if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
    2945           0 :                 saveMapMeta();
    2946             :         }
    2947             : 
    2948             :         // Profile modified reasons
    2949           0 :         Profiler modprofiler;
    2950             : 
    2951           0 :         u32 sector_meta_count = 0;
    2952           0 :         u32 block_count = 0;
    2953           0 :         u32 block_count_all = 0; // Number of blocks in memory
    2954             : 
    2955             :         // Don't do anything with sqlite unless something is really saved
    2956           0 :         bool save_started = false;
    2957             : 
    2958           0 :         for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
    2959           0 :                 i != m_sectors.end(); ++i) {
    2960           0 :                 ServerMapSector *sector = (ServerMapSector*)i->second;
    2961             :                 assert(sector->getId() == MAPSECTOR_SERVER);
    2962             : 
    2963           0 :                 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
    2964           0 :                         saveSectorMeta(sector);
    2965           0 :                         sector_meta_count++;
    2966             :                 }
    2967             : 
    2968           0 :                 MapBlockVect blocks;
    2969           0 :                 sector->getBlocks(blocks);
    2970             : 
    2971           0 :                 for(MapBlockVect::iterator j = blocks.begin();
    2972           0 :                         j != blocks.end(); ++j) {
    2973           0 :                         MapBlock *block = *j;
    2974             : 
    2975           0 :                         block_count_all++;
    2976             : 
    2977           0 :                         if(block->getModified() >= (u32)save_level) {
    2978             :                                 // Lazy beginSave()
    2979           0 :                                 if(!save_started) {
    2980           0 :                                         beginSave();
    2981           0 :                                         save_started = true;
    2982             :                                 }
    2983             : 
    2984           0 :                                 modprofiler.add(block->getModifiedReasonString(), 1);
    2985             : 
    2986           0 :                                 saveBlock(block);
    2987           0 :                                 block_count++;
    2988             : 
    2989             :                                 /*infostream<<"ServerMap: Written block ("
    2990             :                                                 <<block->getPos().X<<","
    2991             :                                                 <<block->getPos().Y<<","
    2992             :                                                 <<block->getPos().Z<<")"
    2993             :                                                 <<std::endl;*/
    2994             :                         }
    2995             :                 }
    2996             :         }
    2997             : 
    2998           0 :         if(save_started)
    2999           0 :                 endSave();
    3000             : 
    3001             :         /*
    3002             :                 Only print if something happened or saved whole map
    3003             :         */
    3004           0 :         if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
    3005           0 :                         || block_count != 0) {
    3006           0 :                 infostream<<"ServerMap: Written: "
    3007           0 :                                 <<sector_meta_count<<" sector metadata files, "
    3008           0 :                                 <<block_count<<" block files"
    3009           0 :                                 <<", "<<block_count_all<<" blocks in memory."
    3010           0 :                                 <<std::endl;
    3011           0 :                 PrintInfo(infostream); // ServerMap/ClientMap:
    3012           0 :                 infostream<<"Blocks modified by: "<<std::endl;
    3013           0 :                 modprofiler.print(infostream);
    3014             :         }
    3015             : }
    3016             : 
    3017           0 : void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
    3018             : {
    3019           0 :         if (loadFromFolders()) {
    3020           0 :                 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
    3021           0 :                                 << "all blocks that are stored in flat files." << std::endl;
    3022             :         }
    3023           0 :         dbase->listAllLoadableBlocks(dst);
    3024           0 : }
    3025             : 
    3026           0 : void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
    3027             : {
    3028           0 :         for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
    3029           0 :                 si != m_sectors.end(); ++si)
    3030             :         {
    3031           0 :                 MapSector *sector = si->second;
    3032             : 
    3033           0 :                 MapBlockVect blocks;
    3034           0 :                 sector->getBlocks(blocks);
    3035             : 
    3036           0 :                 for(MapBlockVect::iterator i = blocks.begin();
    3037           0 :                                 i != blocks.end(); ++i) {
    3038           0 :                         v3s16 p = (*i)->getPos();
    3039           0 :                         dst.push_back(p);
    3040             :                 }
    3041             :         }
    3042           0 : }
    3043             : 
    3044           0 : void ServerMap::saveMapMeta()
    3045             : {
    3046           0 :         DSTACK(__FUNCTION_NAME);
    3047             : 
    3048           0 :         createDirs(m_savedir);
    3049             : 
    3050           0 :         std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
    3051           0 :         std::ostringstream oss(std::ios_base::binary);
    3052           0 :         Settings conf;
    3053             : 
    3054           0 :         m_emerge->params.save(conf);
    3055           0 :         conf.writeLines(oss);
    3056             : 
    3057           0 :         oss << "[end_of_params]\n";
    3058             : 
    3059           0 :         if(!fs::safeWriteToFile(fullpath, oss.str())) {
    3060           0 :                 errorstream << "ServerMap::saveMapMeta(): "
    3061           0 :                                 << "could not write " << fullpath << std::endl;
    3062           0 :                 throw FileNotGoodException("Cannot save chunk metadata");
    3063             :         }
    3064             : 
    3065           0 :         m_map_metadata_changed = false;
    3066           0 : }
    3067             : 
    3068           0 : void ServerMap::loadMapMeta()
    3069             : {
    3070           0 :         DSTACK(__FUNCTION_NAME);
    3071             : 
    3072           0 :         Settings conf;
    3073           0 :         std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
    3074             : 
    3075           0 :         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
    3076           0 :         if (!is.good()) {
    3077             :                 errorstream << "ServerMap::loadMapMeta(): "
    3078           0 :                         "could not open " << fullpath << std::endl;
    3079           0 :                 throw FileNotGoodException("Cannot open map metadata");
    3080             :         }
    3081             : 
    3082           0 :         if (!conf.parseConfigLines(is, "[end_of_params]")) {
    3083             :                 throw SerializationError("ServerMap::loadMapMeta(): "
    3084           0 :                                 "[end_of_params] not found!");
    3085             :         }
    3086             : 
    3087           0 :         m_emerge->params.load(conf);
    3088             : 
    3089           0 :         verbosestream << "ServerMap::loadMapMeta(): seed="
    3090           0 :                 << m_emerge->params.seed << std::endl;
    3091           0 : }
    3092             : 
    3093           0 : void ServerMap::saveSectorMeta(ServerMapSector *sector)
    3094             : {
    3095           0 :         DSTACK(__FUNCTION_NAME);
    3096             :         // Format used for writing
    3097           0 :         u8 version = SER_FMT_VER_HIGHEST_WRITE;
    3098             :         // Get destination
    3099           0 :         v2s16 pos = sector->getPos();
    3100           0 :         std::string dir = getSectorDir(pos);
    3101           0 :         createDirs(dir);
    3102             : 
    3103           0 :         std::string fullpath = dir + DIR_DELIM + "meta";
    3104           0 :         std::ostringstream ss(std::ios_base::binary);
    3105             : 
    3106           0 :         sector->serialize(ss, version);
    3107             : 
    3108           0 :         if(!fs::safeWriteToFile(fullpath, ss.str()))
    3109           0 :                 throw FileNotGoodException("Cannot write sector metafile");
    3110             : 
    3111           0 :         sector->differs_from_disk = false;
    3112           0 : }
    3113             : 
    3114           0 : MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
    3115             : {
    3116           0 :         DSTACK(__FUNCTION_NAME);
    3117             :         // Get destination
    3118           0 :         v2s16 p2d = getSectorPos(sectordir);
    3119             : 
    3120           0 :         ServerMapSector *sector = NULL;
    3121             : 
    3122           0 :         std::string fullpath = sectordir + DIR_DELIM + "meta";
    3123           0 :         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
    3124           0 :         if(is.good() == false)
    3125             :         {
    3126             :                 // If the directory exists anyway, it probably is in some old
    3127             :                 // format. Just go ahead and create the sector.
    3128           0 :                 if(fs::PathExists(sectordir))
    3129             :                 {
    3130             :                         /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
    3131             :                                         <<fullpath<<" doesn't exist but directory does."
    3132             :                                         <<" Continuing with a sector with no metadata."
    3133             :                                         <<std::endl;*/
    3134           0 :                         sector = new ServerMapSector(this, p2d, m_gamedef);
    3135           0 :                         m_sectors[p2d] = sector;
    3136             :                 }
    3137             :                 else
    3138             :                 {
    3139           0 :                         throw FileNotGoodException("Cannot open sector metafile");
    3140             :                 }
    3141             :         }
    3142             :         else
    3143             :         {
    3144             :                 sector = ServerMapSector::deSerialize
    3145           0 :                                 (is, this, p2d, m_sectors, m_gamedef);
    3146           0 :                 if(save_after_load)
    3147           0 :                         saveSectorMeta(sector);
    3148             :         }
    3149             : 
    3150           0 :         sector->differs_from_disk = false;
    3151             : 
    3152           0 :         return sector;
    3153             : }
    3154             : 
    3155           0 : bool ServerMap::loadSectorMeta(v2s16 p2d)
    3156             : {
    3157           0 :         DSTACK(__FUNCTION_NAME);
    3158             : 
    3159             :         // The directory layout we're going to load from.
    3160             :         //  1 - original sectors/xxxxzzzz/
    3161             :         //  2 - new sectors2/xxx/zzz/
    3162             :         //  If we load from anything but the latest structure, we will
    3163             :         //  immediately save to the new one, and remove the old.
    3164           0 :         int loadlayout = 1;
    3165           0 :         std::string sectordir1 = getSectorDir(p2d, 1);
    3166           0 :         std::string sectordir;
    3167           0 :         if(fs::PathExists(sectordir1))
    3168             :         {
    3169           0 :                 sectordir = sectordir1;
    3170             :         }
    3171             :         else
    3172             :         {
    3173           0 :                 loadlayout = 2;
    3174           0 :                 sectordir = getSectorDir(p2d, 2);
    3175             :         }
    3176             : 
    3177             :         try{
    3178           0 :                 loadSectorMeta(sectordir, loadlayout != 2);
    3179             :         }
    3180           0 :         catch(InvalidFilenameException &e)
    3181             :         {
    3182           0 :                 return false;
    3183             :         }
    3184           0 :         catch(FileNotGoodException &e)
    3185             :         {
    3186           0 :                 return false;
    3187             :         }
    3188           0 :         catch(std::exception &e)
    3189             :         {
    3190           0 :                 return false;
    3191             :         }
    3192             : 
    3193           0 :         return true;
    3194             : }
    3195             : 
    3196             : #if 0
    3197             : bool ServerMap::loadSectorFull(v2s16 p2d)
    3198             : {
    3199             :         DSTACK(__FUNCTION_NAME);
    3200             : 
    3201             :         MapSector *sector = NULL;
    3202             : 
    3203             :         // The directory layout we're going to load from.
    3204             :         //  1 - original sectors/xxxxzzzz/
    3205             :         //  2 - new sectors2/xxx/zzz/
    3206             :         //  If we load from anything but the latest structure, we will
    3207             :         //  immediately save to the new one, and remove the old.
    3208             :         int loadlayout = 1;
    3209             :         std::string sectordir1 = getSectorDir(p2d, 1);
    3210             :         std::string sectordir;
    3211             :         if(fs::PathExists(sectordir1))
    3212             :         {
    3213             :                 sectordir = sectordir1;
    3214             :         }
    3215             :         else
    3216             :         {
    3217             :                 loadlayout = 2;
    3218             :                 sectordir = getSectorDir(p2d, 2);
    3219             :         }
    3220             : 
    3221             :         try{
    3222             :                 sector = loadSectorMeta(sectordir, loadlayout != 2);
    3223             :         }
    3224             :         catch(InvalidFilenameException &e)
    3225             :         {
    3226             :                 return false;
    3227             :         }
    3228             :         catch(FileNotGoodException &e)
    3229             :         {
    3230             :                 return false;
    3231             :         }
    3232             :         catch(std::exception &e)
    3233             :         {
    3234             :                 return false;
    3235             :         }
    3236             : 
    3237             :         /*
    3238             :                 Load blocks
    3239             :         */
    3240             :         std::vector<fs::DirListNode> list2 = fs::GetDirListing
    3241             :                         (sectordir);
    3242             :         std::vector<fs::DirListNode>::iterator i2;
    3243             :         for(i2=list2.begin(); i2!=list2.end(); i2++)
    3244             :         {
    3245             :                 // We want files
    3246             :                 if(i2->dir)
    3247             :                         continue;
    3248             :                 try{
    3249             :                         loadBlock(sectordir, i2->name, sector, loadlayout != 2);
    3250             :                 }
    3251             :                 catch(InvalidFilenameException &e)
    3252             :                 {
    3253             :                         // This catches unknown crap in directory
    3254             :                 }
    3255             :         }
    3256             : 
    3257             :         if(loadlayout != 2)
    3258             :         {
    3259             :                 infostream<<"Sector converted to new layout - deleting "<<
    3260             :                         sectordir1<<std::endl;
    3261             :                 fs::RecursiveDelete(sectordir1);
    3262             :         }
    3263             : 
    3264             :         return true;
    3265             : }
    3266             : #endif
    3267             : 
    3268           0 : Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
    3269             : {
    3270           0 :         if (name == "sqlite3")
    3271           0 :                 return new Database_SQLite3(savedir);
    3272           0 :         if (name == "dummy")
    3273           0 :                 return new Database_Dummy();
    3274             :         #if USE_LEVELDB
    3275             :         else if (name == "leveldb")
    3276             :                 return new Database_LevelDB(savedir);
    3277             :         #endif
    3278             :         #if USE_REDIS
    3279             :         else if (name == "redis")
    3280             :                 return new Database_Redis(conf);
    3281             :         #endif
    3282             :         else
    3283           0 :                 throw BaseException(std::string("Database backend ") + name + " not supported.");
    3284             : }
    3285             : 
    3286           0 : void ServerMap::beginSave()
    3287             : {
    3288           0 :         dbase->beginSave();
    3289           0 : }
    3290             : 
    3291           0 : void ServerMap::endSave()
    3292             : {
    3293           0 :         dbase->endSave();
    3294           0 : }
    3295             : 
    3296           0 : bool ServerMap::saveBlock(MapBlock *block)
    3297             : {
    3298           0 :         return saveBlock(block, dbase);
    3299             : }
    3300             : 
    3301           0 : bool ServerMap::saveBlock(MapBlock *block, Database *db)
    3302             : {
    3303           0 :         v3s16 p3d = block->getPos();
    3304             : 
    3305             :         // Dummy blocks are not written
    3306           0 :         if (block->isDummy()) {
    3307           0 :                 errorstream << "WARNING: saveBlock: Not writing dummy block "
    3308           0 :                         << PP(p3d) << std::endl;
    3309           0 :                 return true;
    3310             :         }
    3311             : 
    3312             :         // Format used for writing
    3313           0 :         u8 version = SER_FMT_VER_HIGHEST_WRITE;
    3314             : 
    3315             :         /*
    3316             :                 [0] u8 serialization version
    3317             :                 [1] data
    3318             :         */
    3319           0 :         std::ostringstream o(std::ios_base::binary);
    3320           0 :         o.write((char*) &version, 1);
    3321           0 :         block->serialize(o, version, true);
    3322             : 
    3323           0 :         std::string data = o.str();
    3324           0 :         bool ret = db->saveBlock(p3d, data);
    3325           0 :         if (ret) {
    3326             :                 // We just wrote it to the disk so clear modified flag
    3327           0 :                 block->resetModified();
    3328             :         }
    3329           0 :         return ret;
    3330             : }
    3331             : 
    3332           0 : void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
    3333             :                 MapSector *sector, bool save_after_load)
    3334             : {
    3335           0 :         DSTACK(__FUNCTION_NAME);
    3336             : 
    3337           0 :         std::string fullpath = sectordir + DIR_DELIM + blockfile;
    3338             :         try {
    3339             : 
    3340           0 :                 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
    3341           0 :                 if(is.good() == false)
    3342           0 :                         throw FileNotGoodException("Cannot open block file");
    3343             : 
    3344           0 :                 v3s16 p3d = getBlockPos(sectordir, blockfile);
    3345           0 :                 v2s16 p2d(p3d.X, p3d.Z);
    3346             : 
    3347             :                 assert(sector->getPos() == p2d);
    3348             : 
    3349           0 :                 u8 version = SER_FMT_VER_INVALID;
    3350           0 :                 is.read((char*)&version, 1);
    3351             : 
    3352           0 :                 if(is.fail())
    3353             :                         throw SerializationError("ServerMap::loadBlock(): Failed"
    3354           0 :                                         " to read MapBlock version");
    3355             : 
    3356             :                 /*u32 block_size = MapBlock::serializedLength(version);
    3357             :                 SharedBuffer<u8> data(block_size);
    3358             :                 is.read((char*)*data, block_size);*/
    3359             : 
    3360             :                 // This will always return a sector because we're the server
    3361             :                 //MapSector *sector = emergeSector(p2d);
    3362             : 
    3363           0 :                 MapBlock *block = NULL;
    3364           0 :                 bool created_new = false;
    3365           0 :                 block = sector->getBlockNoCreateNoEx(p3d.Y);
    3366           0 :                 if(block == NULL)
    3367             :                 {
    3368           0 :                         block = sector->createBlankBlockNoInsert(p3d.Y);
    3369           0 :                         created_new = true;
    3370             :                 }
    3371             : 
    3372             :                 // Read basic data
    3373           0 :                 block->deSerialize(is, version, true);
    3374             : 
    3375             :                 // If it's a new block, insert it to the map
    3376           0 :                 if(created_new)
    3377           0 :                         sector->insertBlock(block);
    3378             : 
    3379             :                 /*
    3380             :                         Save blocks loaded in old format in new format
    3381             :                 */
    3382             : 
    3383           0 :                 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
    3384             :                 {
    3385           0 :                         saveBlock(block);
    3386             : 
    3387             :                         // Should be in database now, so delete the old file
    3388           0 :                         fs::RecursiveDelete(fullpath);
    3389             :                 }
    3390             : 
    3391             :                 // We just loaded it from the disk, so it's up-to-date.
    3392           0 :                 block->resetModified();
    3393             : 
    3394             :         }
    3395           0 :         catch(SerializationError &e)
    3396             :         {
    3397           0 :                 infostream<<"WARNING: Invalid block data on disk "
    3398           0 :                                 <<"fullpath="<<fullpath
    3399           0 :                                 <<" (SerializationError). "
    3400           0 :                                 <<"what()="<<e.what()
    3401           0 :                                 <<std::endl;
    3402             :                                 // Ignoring. A new one will be generated.
    3403           0 :                 abort();
    3404             : 
    3405             :                 // TODO: Backup file; name is in fullpath.
    3406             :         }
    3407           0 : }
    3408             : 
    3409           0 : void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
    3410             : {
    3411           0 :         DSTACK(__FUNCTION_NAME);
    3412             : 
    3413             :         try {
    3414           0 :                 std::istringstream is(*blob, std::ios_base::binary);
    3415             : 
    3416           0 :                 u8 version = SER_FMT_VER_INVALID;
    3417           0 :                 is.read((char*)&version, 1);
    3418             : 
    3419           0 :                 if(is.fail())
    3420             :                         throw SerializationError("ServerMap::loadBlock(): Failed"
    3421           0 :                                         " to read MapBlock version");
    3422             : 
    3423             :                 /*u32 block_size = MapBlock::serializedLength(version);
    3424             :                 SharedBuffer<u8> data(block_size);
    3425             :                 is.read((char*)*data, block_size);*/
    3426             : 
    3427             :                 // This will always return a sector because we're the server
    3428             :                 //MapSector *sector = emergeSector(p2d);
    3429             : 
    3430           0 :                 MapBlock *block = NULL;
    3431           0 :                 bool created_new = false;
    3432           0 :                 block = sector->getBlockNoCreateNoEx(p3d.Y);
    3433           0 :                 if(block == NULL)
    3434             :                 {
    3435           0 :                         block = sector->createBlankBlockNoInsert(p3d.Y);
    3436           0 :                         created_new = true;
    3437             :                 }
    3438             : 
    3439             :                 // Read basic data
    3440           0 :                 block->deSerialize(is, version, true);
    3441             : 
    3442             :                 // If it's a new block, insert it to the map
    3443           0 :                 if(created_new)
    3444           0 :                         sector->insertBlock(block);
    3445             : 
    3446             :                 /*
    3447             :                         Save blocks loaded in old format in new format
    3448             :                 */
    3449             : 
    3450             :                 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
    3451             :                 // Only save if asked to; no need to update version
    3452           0 :                 if(save_after_load)
    3453           0 :                         saveBlock(block);
    3454             : 
    3455             :                 // We just loaded it from, so it's up-to-date.
    3456           0 :                 block->resetModified();
    3457             : 
    3458             :         }
    3459           0 :         catch(SerializationError &e)
    3460             :         {
    3461           0 :                 errorstream<<"Invalid block data in database"
    3462           0 :                                 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
    3463           0 :                                 <<" (SerializationError): "<<e.what()<<std::endl;
    3464             : 
    3465             :                 // TODO: Block should be marked as invalid in memory so that it is
    3466             :                 // not touched but the game can run
    3467             : 
    3468           0 :                 if(g_settings->getBool("ignore_world_load_errors")){
    3469           0 :                         errorstream<<"Ignoring block load error. Duck and cover! "
    3470           0 :                                         <<"(ignore_world_load_errors)"<<std::endl;
    3471             :                 } else {
    3472           0 :                         throw SerializationError("Invalid block data in database");
    3473             :                 }
    3474             :         }
    3475           0 : }
    3476             : 
    3477           0 : MapBlock* ServerMap::loadBlock(v3s16 blockpos)
    3478             : {
    3479           0 :         DSTACK(__FUNCTION_NAME);
    3480             : 
    3481           0 :         v2s16 p2d(blockpos.X, blockpos.Z);
    3482             : 
    3483           0 :         std::string ret;
    3484             : 
    3485           0 :         ret = dbase->loadBlock(blockpos);
    3486           0 :         if (ret != "") {
    3487           0 :                 loadBlock(&ret, blockpos, createSector(p2d), false);
    3488           0 :                 return getBlockNoCreateNoEx(blockpos);
    3489             :         }
    3490             :         // Not found in database, try the files
    3491             : 
    3492             :         // The directory layout we're going to load from.
    3493             :         //  1 - original sectors/xxxxzzzz/
    3494             :         //  2 - new sectors2/xxx/zzz/
    3495             :         //  If we load from anything but the latest structure, we will
    3496             :         //  immediately save to the new one, and remove the old.
    3497           0 :         int loadlayout = 1;
    3498           0 :         std::string sectordir1 = getSectorDir(p2d, 1);
    3499           0 :         std::string sectordir;
    3500           0 :         if(fs::PathExists(sectordir1))
    3501             :         {
    3502           0 :                 sectordir = sectordir1;
    3503             :         }
    3504             :         else
    3505             :         {
    3506           0 :                 loadlayout = 2;
    3507           0 :                 sectordir = getSectorDir(p2d, 2);
    3508             :         }
    3509             : 
    3510             :         /*
    3511             :                 Make sure sector is loaded
    3512             :         */
    3513           0 :         MapSector *sector = getSectorNoGenerateNoEx(p2d);
    3514           0 :         if(sector == NULL)
    3515             :         {
    3516             :                 try{
    3517           0 :                         sector = loadSectorMeta(sectordir, loadlayout != 2);
    3518             :                 }
    3519           0 :                 catch(InvalidFilenameException &e)
    3520             :                 {
    3521           0 :                         return NULL;
    3522             :                 }
    3523           0 :                 catch(FileNotGoodException &e)
    3524             :                 {
    3525           0 :                         return NULL;
    3526             :                 }
    3527           0 :                 catch(std::exception &e)
    3528             :                 {
    3529           0 :                         return NULL;
    3530             :                 }
    3531             :         }
    3532             : 
    3533             :         /*
    3534             :                 Make sure file exists
    3535             :         */
    3536             : 
    3537           0 :         std::string blockfilename = getBlockFilename(blockpos);
    3538           0 :         if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
    3539           0 :                 return NULL;
    3540             : 
    3541             :         /*
    3542             :                 Load block and save it to the database
    3543             :         */
    3544           0 :         loadBlock(sectordir, blockfilename, sector, true);
    3545           0 :         return getBlockNoCreateNoEx(blockpos);
    3546             : }
    3547             : 
    3548           0 : bool ServerMap::deleteBlock(v3s16 blockpos)
    3549             : {
    3550           0 :         if (!dbase->deleteBlock(blockpos))
    3551           0 :                 return false;
    3552             : 
    3553           0 :         MapBlock *block = getBlockNoCreateNoEx(blockpos);
    3554           0 :         if (block) {
    3555           0 :                 v2s16 p2d(blockpos.X, blockpos.Z);
    3556           0 :                 MapSector *sector = getSectorNoGenerateNoEx(p2d);
    3557           0 :                 if (!sector)
    3558           0 :                         return false;
    3559           0 :                 sector->deleteBlock(block);
    3560             :         }
    3561             : 
    3562           0 :         return true;
    3563             : }
    3564             : 
    3565           0 : void ServerMap::PrintInfo(std::ostream &out)
    3566             : {
    3567           0 :         out<<"ServerMap: ";
    3568           0 : }
    3569             : 
    3570           0 : MMVManip::MMVManip(Map *map):
    3571             :                 VoxelManipulator(),
    3572             :                 m_is_dirty(false),
    3573             :                 m_create_area(false),
    3574           0 :                 m_map(map)
    3575             : {
    3576           0 : }
    3577             : 
    3578           0 : MMVManip::~MMVManip()
    3579             : {
    3580           0 : }
    3581             : 
    3582           0 : void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
    3583             :         bool load_if_inexistent)
    3584             : {
    3585           0 :         TimeTaker timer1("initialEmerge", &emerge_time);
    3586             : 
    3587             :         // Units of these are MapBlocks
    3588           0 :         v3s16 p_min = blockpos_min;
    3589           0 :         v3s16 p_max = blockpos_max;
    3590             : 
    3591             :         VoxelArea block_area_nodes
    3592           0 :                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
    3593             : 
    3594           0 :         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
    3595           0 :         if(size_MB >= 1)
    3596             :         {
    3597           0 :                 infostream<<"initialEmerge: area: ";
    3598           0 :                 block_area_nodes.print(infostream);
    3599           0 :                 infostream<<" ("<<size_MB<<"MB)";
    3600           0 :                 infostream<<std::endl;
    3601             :         }
    3602             : 
    3603           0 :         addArea(block_area_nodes);
    3604             : 
    3605           0 :         for(s32 z=p_min.Z; z<=p_max.Z; z++)
    3606           0 :         for(s32 y=p_min.Y; y<=p_max.Y; y++)
    3607           0 :         for(s32 x=p_min.X; x<=p_max.X; x++)
    3608             :         {
    3609           0 :                 u8 flags = 0;
    3610             :                 MapBlock *block;
    3611           0 :                 v3s16 p(x,y,z);
    3612           0 :                 std::map<v3s16, u8>::iterator n;
    3613           0 :                 n = m_loaded_blocks.find(p);
    3614           0 :                 if(n != m_loaded_blocks.end())
    3615           0 :                         continue;
    3616             : 
    3617           0 :                 bool block_data_inexistent = false;
    3618             :                 try
    3619             :                 {
    3620           0 :                         TimeTaker timer1("emerge load", &emerge_load_time);
    3621             : 
    3622           0 :                         block = m_map->getBlockNoCreate(p);
    3623           0 :                         if(block->isDummy())
    3624           0 :                                 block_data_inexistent = true;
    3625             :                         else
    3626           0 :                                 block->copyTo(*this);
    3627             :                 }
    3628           0 :                 catch(InvalidPositionException &e)
    3629             :                 {
    3630           0 :                         block_data_inexistent = true;
    3631             :                 }
    3632             : 
    3633           0 :                 if(block_data_inexistent)
    3634             :                 {
    3635             : 
    3636           0 :                         if (load_if_inexistent) {
    3637           0 :                                 ServerMap *svrmap = (ServerMap *)m_map;
    3638           0 :                                 block = svrmap->emergeBlock(p, false);
    3639           0 :                                 if (block == NULL)
    3640           0 :                                         block = svrmap->createBlock(p);
    3641           0 :                                 block->copyTo(*this);
    3642             :                         } else {
    3643           0 :                                 flags |= VMANIP_BLOCK_DATA_INEXIST;
    3644             : 
    3645             :                                 /*
    3646             :                                         Mark area inexistent
    3647             :                                 */
    3648           0 :                                 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
    3649             :                                 // Fill with VOXELFLAG_NO_DATA
    3650           0 :                                 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
    3651           0 :                                 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
    3652             :                                 {
    3653           0 :                                         s32 i = m_area.index(a.MinEdge.X,y,z);
    3654           0 :                                         memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
    3655             :                                 }
    3656             :                         }
    3657             :                 }
    3658             :                 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
    3659             :                 {
    3660             :                         // Mark that block was loaded as blank
    3661             :                         flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
    3662             :                 }*/
    3663             : 
    3664           0 :                 m_loaded_blocks[p] = flags;
    3665             :         }
    3666             : 
    3667           0 :         m_is_dirty = false;
    3668           0 : }
    3669             : 
    3670           0 : void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
    3671             :         bool overwrite_generated)
    3672             : {
    3673           0 :         if(m_area.getExtent() == v3s16(0,0,0))
    3674           0 :                 return;
    3675             : 
    3676             :         /*
    3677             :                 Copy data of all blocks
    3678             :         */
    3679           0 :         for(std::map<v3s16, u8>::iterator
    3680           0 :                         i = m_loaded_blocks.begin();
    3681           0 :                         i != m_loaded_blocks.end(); ++i)
    3682             :         {
    3683           0 :                 v3s16 p = i->first;
    3684           0 :                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
    3685           0 :                 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
    3686           0 :                 if ((existed == false) || (block == NULL) ||
    3687           0 :                         (overwrite_generated == false && block->isGenerated() == true))
    3688           0 :                         continue;
    3689             : 
    3690           0 :                 block->copyFrom(*this);
    3691             : 
    3692           0 :                 if(modified_blocks)
    3693           0 :                         (*modified_blocks)[p] = block;
    3694             :         }
    3695           3 : }
    3696             : 
    3697             : //END

Generated by: LCOV version 1.11