]> git.lizzy.rs Git - minetest.git/blobdiff - src/map.cpp
Make m_media_fetch_threads to contain MediaFetchThread* instead of MediaFetchThread
[minetest.git] / src / map.cpp
index b3954019b84951b0691aa591d710532a10af3bd6..2845f3a67de7ed93228982da25769242ee9f5f2e 100644 (file)
@@ -3,16 +3,16 @@ Minetest-c55
 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GNU Lesser General Public License for more details.
 
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU Lesser General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
@@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "main.h"
 #include "filesys.h"
-#include "utility.h"
 #include "voxel.h"
 #include "porting.h"
 #include "mapgen.h"
@@ -32,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "profiler.h"
 #include "nodedef.h"
 #include "gamedef.h"
+#include "util/directiontables.h"
+#include "rollback_interface.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -691,6 +692,11 @@ void Map::updateLighting(enum LightBank bank,
 
        core::map<v3s16, u8> unlight_from;
 
+       int num_bottom_invalid = 0;
+       
+       {
+       //TimeTaker t("first stuff");
+
        core::map<v3s16, MapBlock*>::Iterator i;
        i = a_blocks.getIterator();
        for(; i.atEnd() == false; i++)
@@ -704,6 +710,7 @@ void Map::updateLighting(enum LightBank bank,
                                break;
 
                        v3s16 pos = block->getPos();
+                       v3s16 posnodes = block->getPosRelative();
                        modified_blocks.insert(pos, block);
 
                        blocks_to_update.insert(pos, block);
@@ -718,20 +725,23 @@ void Map::updateLighting(enum LightBank bank,
 
                                try{
                                        v3s16 p(x,y,z);
-                                       MapNode n = block->getNode(v3s16(x,y,z));
+                                       MapNode n = block->getNode(p);
                                        u8 oldlight = n.getLight(bank, nodemgr);
                                        n.setLight(bank, 0, nodemgr);
-                                       block->setNode(v3s16(x,y,z), n);
+                                       block->setNode(p, n);
+
+                                       // If node sources light, add to list
+                                       u8 source = nodemgr->get(n).light_source;
+                                       if(source != 0)
+                                               light_sources[p + posnodes] = true;
 
                                        // Collect borders for unlighting
-                                       if(x==0 || x == MAP_BLOCKSIZE-1
+                                       if((x==0 || x == MAP_BLOCKSIZE-1
                                        || y==0 || y == MAP_BLOCKSIZE-1
                                        || z==0 || z == MAP_BLOCKSIZE-1)
+                                       && oldlight != 0)
                                        {
-                                               v3s16 p_map = p + v3s16(
-                                                               MAP_BLOCKSIZE*pos.X,
-                                                               MAP_BLOCKSIZE*pos.Y,
-                                                               MAP_BLOCKSIZE*pos.Z);
+                                               v3s16 p_map = p + posnodes;
                                                unlight_from.insert(p_map, oldlight);
                                        }
                                }
@@ -751,6 +761,9 @@ void Map::updateLighting(enum LightBank bank,
                        {
                                bool bottom_valid = block->propagateSunlight(light_sources);
 
+                               if(!bottom_valid)
+                                       num_bottom_invalid++;
+
                                // If bottom is valid, we're done.
                                if(bottom_valid)
                                        break;
@@ -783,7 +796,9 @@ void Map::updateLighting(enum LightBank bank,
 
                }
        }
-       
+
+       }
+
        /*
                Enable this to disable proper lighting for speeding up map
                generation for testing or whatever
@@ -803,38 +818,43 @@ void Map::updateLighting(enum LightBank bank,
        }
 #endif
 
-#if 0
+#if 1
        {
-               TimeTaker timer("unspreadLight");
+               //TimeTaker timer("unspreadLight");
                unspreadLight(bank, unlight_from, light_sources, modified_blocks);
        }
 
-       if(debug)
+       /*if(debug)
        {
                u32 diff = modified_blocks.size() - count_was;
                count_was = modified_blocks.size();
                infostream<<"unspreadLight modified "<<diff<<std::endl;
-       }
+       }*/
 
        {
-               TimeTaker timer("spreadLight");
+               //TimeTaker timer("spreadLight");
                spreadLight(bank, light_sources, modified_blocks);
        }
 
-       if(debug)
+       /*if(debug)
        {
                u32 diff = modified_blocks.size() - count_was;
                count_was = modified_blocks.size();
                infostream<<"spreadLight modified "<<diff<<std::endl;
-       }
+       }*/
 #endif
 
+#if 0
        {
                //MapVoxelManipulator vmanip(this);
-
+               
                // Make a manual voxel manipulator and load all the blocks
                // that touch the requested blocks
                ManualMapVoxelManipulator vmanip(this);
+
+               {
+               //TimeTaker timer("initialEmerge");
+
                core::map<v3s16, MapBlock*>::Iterator i;
                i = blocks_to_update.getIterator();
                for(; i.atEnd() == false; i++)
@@ -868,6 +888,7 @@ void Map::updateLighting(enum LightBank bank,
                        // Lighting of block will be updated completely
                        block->setLightingExpired(false);
                }
+               }
 
                {
                        //TimeTaker timer("unSpreadLight");
@@ -884,6 +905,7 @@ void Map::updateLighting(enum LightBank bank,
                /*infostream<<"emerge_time="<<emerge_time<<std::endl;
                emerge_time = 0;*/
        }
+#endif
 
        //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
 }
@@ -902,7 +924,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
                        i.atEnd() == false; i++)
        {
                MapBlock *block = i.getNode()->getValue();
-               block->updateDayNightDiff();
+               block->expireDayNightDiff();
        }
 }
 
@@ -911,12 +933,12 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                core::map<v3s16, MapBlock*> &modified_blocks)
 {
-       INodeDefManager *nodemgr = m_gamedef->ndef();
+       INodeDefManager *ndef = m_gamedef->ndef();
 
        /*PrintInfo(m_dout);
        m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
                        <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
+       
        /*
                From this node to nodes underneath:
                If lighting is sunlight (1.0), unlight neighbours and
@@ -929,6 +951,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 
        bool node_under_sunlight = true;
        core::map<v3s16, bool> light_sources;
+       
+       /*
+               Collect old node for rollback
+       */
+       RollbackNode rollback_oldnode(this, p, m_gamedef);
 
        /*
                If there is a node at top and it doesn't have sunlight,
@@ -939,7 +966,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        try{
                MapNode topnode = getNode(toppos);
 
-               if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
+               if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
                        node_under_sunlight = false;
        }
        catch(InvalidPositionException &e)
@@ -959,7 +986,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        {
                enum LightBank bank = banks[i];
 
-               u8 lightwas = getNode(p).getLight(bank, nodemgr);
+               u8 lightwas = getNode(p).getLight(bank, ndef);
 
                // Add the block of the added node to modified_blocks
                v3s16 blockpos = getNodeBlockPos(p);
@@ -976,38 +1003,29 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                // light again into this.
                unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
 
-               n.setLight(bank, 0, nodemgr);
+               n.setLight(bank, 0, ndef);
        }
 
        /*
                If node lets sunlight through and is under sunlight, it has
                sunlight too.
        */
-       if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
+       if(node_under_sunlight && ndef->get(n).sunlight_propagates)
        {
-               n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
+               n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
        }
 
        /*
-               Set the node on the map
+               Remove node metadata
        */
 
-       setNode(p, n);
+       removeNodeMetadata(p);
 
        /*
-               Add intial metadata
+               Set the node on the map
        */
-       
-       std::string metadata_name = nodemgr->get(n).metadata_name;
-       if(metadata_name != ""){
-               NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
-               if(!meta){
-                       errorstream<<"Failed to create node metadata \""
-                                       <<metadata_name<<"\""<<std::endl;
-               } else {
-                       setNodeMetadata(p, meta);
-               }
-       }
+
+       setNode(p, n);
 
        /*
                If node is under sunlight and doesn't let sunlight through,
@@ -1016,7 +1034,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                TODO: This could be optimized by mass-unlighting instead
                          of looping
        */
-       if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
+       if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
        {
                s16 y = p.Y - 1;
                for(;; y--){
@@ -1032,12 +1050,12 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                                break;
                        }
 
-                       if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
+                       if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
                        {
                                unLightNeighbors(LIGHTBANK_DAY,
-                                               n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
+                                               n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
                                                light_sources, modified_blocks);
-                               n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
+                               n2.setLight(LIGHTBANK_DAY, 0, ndef);
                                setNode(n2pos, n2);
                        }
                        else
@@ -1063,7 +1081,18 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                        i.atEnd() == false; i++)
        {
                MapBlock *block = i.getNode()->getValue();
-               block->updateDayNightDiff();
+               block->expireDayNightDiff();
+       }
+
+       /*
+               Report for rollback
+       */
+       if(m_gamedef->rollback())
+       {
+               RollbackNode rollback_newnode(this, p, m_gamedef);
+               RollbackAction action;
+               action.setSetNode(p, rollback_oldnode, rollback_newnode);
+               m_gamedef->rollback()->reportAction(action);
        }
 
        /*
@@ -1087,7 +1116,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
+               if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1103,7 +1132,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 void Map::removeNodeAndUpdate(v3s16 p,
                core::map<v3s16, MapBlock*> &modified_blocks)
 {
-       INodeDefManager *nodemgr = m_gamedef->ndef();
+       INodeDefManager *ndef = m_gamedef->ndef();
 
        /*PrintInfo(m_dout);
        m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
@@ -1116,6 +1145,11 @@ void Map::removeNodeAndUpdate(v3s16 p,
        // Node will be replaced with this
        content_t replace_material = CONTENT_AIR;
 
+       /*
+               Collect old node for rollback
+       */
+       RollbackNode rollback_oldnode(this, p, m_gamedef);
+
        /*
                If there is a node at top and it doesn't have sunlight,
                there will be no sunlight going down.
@@ -1123,7 +1157,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        try{
                MapNode topnode = getNode(toppos);
 
-               if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
+               if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
                        node_under_sunlight = false;
        }
        catch(InvalidPositionException &e)
@@ -1145,7 +1179,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                        Unlight neighbors (in case the node is a light source)
                */
                unLightNeighbors(bank, p,
-                               getNode(p).getLight(bank, nodemgr),
+                               getNode(p).getLight(bank, ndef),
                                light_sources, modified_blocks);
        }
 
@@ -1207,7 +1241,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                // TODO: Is this needed? Lighting is cleared up there already.
                try{
                        MapNode n = getNode(p);
-                       n.setLight(LIGHTBANK_DAY, 0, nodemgr);
+                       n.setLight(LIGHTBANK_DAY, 0, ndef);
                        setNode(p, n);
                }
                catch(InvalidPositionException &e)
@@ -1239,7 +1273,18 @@ void Map::removeNodeAndUpdate(v3s16 p,
                        i.atEnd() == false; i++)
        {
                MapBlock *block = i.getNode()->getValue();
-               block->updateDayNightDiff();
+               block->expireDayNightDiff();
+       }
+
+       /*
+               Report for rollback
+       */
+       if(m_gamedef->rollback())
+       {
+               RollbackNode rollback_newnode(this, p, m_gamedef);
+               RollbackAction action;
+               action.setSetNode(p, rollback_oldnode, rollback_newnode);
+               m_gamedef->rollback()->reportAction(action);
        }
 
        /*
@@ -1263,7 +1308,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
+               if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1331,12 +1376,12 @@ bool Map::removeNodeWithEvent(v3s16 p)
        return succeeded;
 }
 
-bool Map::dayNightDiffed(v3s16 blockpos)
+bool Map::getDayNightDiff(v3s16 blockpos)
 {
        try{
                v3s16 p = blockpos + v3s16(0,0,0);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
@@ -1344,21 +1389,21 @@ bool Map::dayNightDiffed(v3s16 blockpos)
        try{
                v3s16 p = blockpos + v3s16(-1,0,0);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,-1,0);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,0,-1);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
@@ -1366,21 +1411,21 @@ bool Map::dayNightDiffed(v3s16 blockpos)
        try{
                v3s16 p = blockpos + v3s16(1,0,0);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,1,0);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,0,1);
                MapBlock *b = getBlockNoCreate(p);
-               if(b->dayNightDiffed())
+               if(b->getDayNightDiff())
                        return true;
        }
        catch(InvalidPositionException &e){}
@@ -1423,8 +1468,8 @@ void Map::timerUpdate(float dtime, float unload_timeout,
                        MapBlock *block = (*i);
                        
                        block->incrementUsageTimer(dtime);
-                       
-                       if(block->getUsageTimer() > unload_timeout)
+
+                       if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
                        {
                                v3s16 p = block->getPos();
 
@@ -1591,7 +1636,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
        while(m_transforming_liquid.size() != 0)
        {
                // This should be done here so that it is done when continue is used
-               if(loopcount >= initial_size * 3)
+               if(loopcount >= initial_size || loopcount >= 10000)
                        break;
                loopcount++;
 
@@ -1701,12 +1746,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                content_t new_node_content;
                s8 new_node_level = -1;
                s8 max_node_level = -1;
-               if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
+               if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
                        // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
                        // or the flowing alternative of the first of the surrounding sources (if it's air), so
                        // it's perfectly safe to use liquid_kind here to determine the new node content.
                        new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
-               } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
+               } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
                        // liquid_kind is set properly, see above
                        new_node_content = liquid_kind;
                        max_node_level = new_node_level = LIQUID_LEVEL_MAX;
@@ -1779,7 +1824,30 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                        n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
                }
                n0.setContent(new_node_content);
-               setNode(p0, n0);
+               
+               // Find out whether there is a suspect for this action
+               std::string suspect;
+               if(m_gamedef->rollback()){
+                       suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
+               }
+
+               if(!suspect.empty()){
+                       // Blame suspect
+                       RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
+                       // Get old node for rollback
+                       RollbackNode rollback_oldnode(this, p0, m_gamedef);
+                       // Set node
+                       setNode(p0, n0);
+                       // Report
+                       RollbackNode rollback_newnode(this, p0, m_gamedef);
+                       RollbackAction action;
+                       action.setSetNode(p0, rollback_oldnode, rollback_newnode);
+                       m_gamedef->rollback()->reportAction(action);
+               } else {
+                       // Set node
+                       setNode(p0, n0);
+               }
+
                v3s16 blockpos = getNodeBlockPos(p0);
                MapBlock *block = getBlockNoCreateNoEx(blockpos);
                if(block != NULL) {
@@ -1832,7 +1900,7 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p)
                                <<std::endl;
                return NULL;
        }
-       NodeMetadata *meta = block->m_node_metadata->get(p_rel);
+       NodeMetadata *meta = block->m_node_metadata.get(p_rel);
        return meta;
 }
 
@@ -1852,7 +1920,7 @@ void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
                                <<std::endl;
                return;
        }
-       block->m_node_metadata->set(p_rel, meta);
+       block->m_node_metadata.set(p_rel, meta);
 }
 
 void Map::removeNodeMetadata(v3s16 p)
@@ -1866,36 +1934,60 @@ void Map::removeNodeMetadata(v3s16 p)
                                <<std::endl;
                return;
        }
-       block->m_node_metadata->remove(p_rel);
+       block->m_node_metadata.remove(p_rel);
 }
 
-void Map::nodeMetadataStep(float dtime,
-               core::map<v3s16, MapBlock*> &changed_blocks)
+NodeTimer Map::getNodeTimer(v3s16 p)
 {
-       /*
-               NOTE:
-               Currently there is no way to ensure that all the necessary
-               blocks are loaded when this is run. (They might get unloaded)
-               NOTE: ^- Actually, that might not be so. In a quick test it
-               reloaded a block with a furnace when I walked back to it from
-               a distance.
-       */
-       core::map<v2s16, MapSector*>::Iterator si;
-       si = m_sectors.getIterator();
-       for(; si.atEnd() == false; si++)
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(!block){
+               infostream<<"Map::getNodeTimer(): Need to emerge "
+                               <<PP(blockpos)<<std::endl;
+               block = emergeBlock(blockpos, false);
+       }
+       if(!block)
        {
-               MapSector *sector = si.getNode()->getValue();
-               core::list< MapBlock * > sectorblocks;
-               sector->getBlocks(sectorblocks);
-               core::list< MapBlock * >::Iterator i;
-               for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
-               {
-                       MapBlock *block = *i;
-                       bool changed = block->m_node_metadata->step(dtime);
-                       if(changed)
-                               changed_blocks[block->getPos()] = block;
-               }
+               infostream<<"WARNING: Map::getNodeTimer(): Block not found"
+                               <<std::endl;
+               return NodeTimer();
        }
+       NodeTimer t = block->m_node_timers.get(p_rel);
+       return t;
+}
+
+void Map::setNodeTimer(v3s16 p, NodeTimer t)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(!block){
+               infostream<<"Map::setNodeTimer(): Need to emerge "
+                               <<PP(blockpos)<<std::endl;
+               block = emergeBlock(blockpos, false);
+       }
+       if(!block)
+       {
+               infostream<<"WARNING: Map::setNodeTimer(): Block not found"
+                               <<std::endl;
+               return;
+       }
+       block->m_node_timers.set(p_rel, t);
+}
+
+void Map::removeNodeTimer(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+       {
+               infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
+                               <<std::endl;
+               return;
+       }
+       block->m_node_timers.remove(p_rel);
 }
 
 /*
@@ -2048,12 +2140,29 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 {
        bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
        if(enable_mapgen_debug_info)
-               infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
-                               <<blockpos.Z<<")"<<std::endl;
+               infostream<<"initBlockMake(): "
+                               <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
+                               <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
+                               <<std::endl;
        
+       //s16 chunksize = 3;
+       //v3s16 chunk_offset(-1,-1,-1);
+       //s16 chunksize = 4;
+       //v3s16 chunk_offset(-1,-1,-1);
+       s16 chunksize = 5;
+       v3s16 chunk_offset(-2,-2,-2);
+       v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
+       v3s16 blockpos_min = blockpos_div * chunksize;
+       v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
+       blockpos_min += chunk_offset;
+       blockpos_max += chunk_offset;
+
+       //v3s16 extra_borders(1,1,1);
+       v3s16 extra_borders(1,1,1);
+
        // Do nothing if not inside limits (+-1 because of neighbors)
-       if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
-               blockpos_over_limit(blockpos + v3s16(1,1,1)))
+       if(blockpos_over_limit(blockpos_min - extra_borders) ||
+               blockpos_over_limit(blockpos_max + extra_borders))
        {
                data->no_op = true;
                return;
@@ -2061,7 +2170,9 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
        
        data->no_op = false;
        data->seed = m_seed;
-       data->blockpos = blockpos;
+       data->blockpos_min = blockpos_min;
+       data->blockpos_max = blockpos_max;
+       data->blockpos_requested = blockpos;
        data->nodedef = m_gamedef->ndef();
 
        /*
@@ -2070,17 +2181,20 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
        {
                //TimeTaker timer("initBlockMake() create area");
                
-               for(s16 x=-1; x<=1; x++)
-               for(s16 z=-1; z<=1; z++)
+               for(s16 x=blockpos_min.X-extra_borders.X;
+                               x<=blockpos_max.X+extra_borders.X; x++)
+               for(s16 z=blockpos_min.Z-extra_borders.Z;
+                               z<=blockpos_max.Z+extra_borders.Z; z++)
                {
-                       v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
+                       v2s16 sectorpos(x, z);
                        // Sector metadata is loaded from disk if not already loaded.
                        ServerMapSector *sector = createSector(sectorpos);
                        assert(sector);
 
-                       for(s16 y=-1; y<=1; y++)
+                       for(s16 y=blockpos_min.Y-extra_borders.Y;
+                                       y<=blockpos_max.Y+extra_borders.Y; y++)
                        {
-                               v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
+                               v3s16 p(x,y,z);
                                //MapBlock *block = createBlock(p);
                                // 1) get from memory, 2) load from disk
                                MapBlock *block = emergeBlock(p, false);
@@ -2114,8 +2228,8 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
        */
        
        // The area that contains this block and it's neighbors
-       v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
-       v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
+       v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
+       v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
        
        data->vmanip = new ManualMapVoxelManipulator(this);
        //data->vmanip->setMap(this);
@@ -2132,9 +2246,14 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                core::map<v3s16, MapBlock*> &changed_blocks)
 {
-       v3s16 blockpos = data->blockpos;
-       /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
-                       <<blockpos.Z<<")"<<std::endl;*/
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       v3s16 blockpos_requested = data->blockpos_requested;
+       /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
+                       <<blockpos_requested.Y<<","
+                       <<blockpos_requested.Z<<")"<<std::endl;*/
+
+       v3s16 extra_borders(1,1,1);
 
        if(data->no_op)
        {
@@ -2148,11 +2267,14 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
        data->vmanip.print(infostream);*/
 
        // Make sure affected blocks are loaded
-       for(s16 x=-1; x<=1; x++)
-       for(s16 z=-1; z<=1; z++)
-       for(s16 y=-1; y<=1; y++)
-       {
-               v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
+       for(s16 x=blockpos_min.X-extra_borders.X;
+                       x<=blockpos_max.X+extra_borders.X; x++)
+       for(s16 z=blockpos_min.Z-extra_borders.Z;
+                       z<=blockpos_max.Z+extra_borders.Z; z++)
+       for(s16 y=blockpos_min.Y-extra_borders.Y;
+                       y<=blockpos_max.Y+extra_borders.Y; y++)
+       {
+               v3s16 p(x, y, z);
                // Load from disk if not already in memory
                emergeBlock(p, false);
        }
@@ -2179,107 +2301,59 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                v3s16 p = data->transforming_liquid.pop_front();
                m_transforming_liquid.push_back(p);
        }
-       
-       /*
-               Get central block
-       */
-       MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
-       assert(block);
-
-       /*
-               Set is_underground flag for lighting with sunlight.
-
-               Refer to map generator heuristics.
-
-               NOTE: This is done in initChunkMake
-       */
-       //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
-
-
-       /*
-               Add sunlight to central block.
-               This makes in-dark-spawning monsters to not flood the whole thing.
-               Do not spread the light, though.
-       */
-       /*core::map<v3s16, bool> light_sources;
-       bool black_air_left = false;
-       block->propagateSunlight(light_sources, true, &black_air_left);*/
 
        /*
-               NOTE: Lighting and object adding shouldn't really be here, but
-               lighting is a bit tricky to move properly to makeBlock.
-               TODO: Do this the right way anyway, that is, move it to makeBlock.
-                     - There needs to be some way for makeBlock to report back if
-                           the lighting update is going further down because of the
-                               new block blocking light
+               Do stuff in central blocks
        */
 
        /*
                Update lighting
-               NOTE: This takes ~60ms, TODO: Investigate why
        */
        {
+#if 0
                TimeTaker t("finishBlockMake lighting update");
 
                core::map<v3s16, MapBlock*> lighting_update_blocks;
-#if 1
-               // Center block
-               lighting_update_blocks.insert(block->getPos(), block);
-
-               /*{
-                       s16 x = 0;
-                       s16 z = 0;
-                       v3s16 p = block->getPos()+v3s16(x,1,z);
-                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
-               }*/
-#endif
-#if 0
-               // All modified blocks
-               // NOTE: Should this be done? If this is not done, then the lighting
-               // of the others will be updated in a different place, one by one, i
-               // think... or they might not? Well, at least they are left marked as
-               // "lighting expired"; it seems that is not handled at all anywhere,
-               // so enabling this will slow it down A LOT because otherwise it
-               // would not do this at all. This causes the black trees.
-               for(core::map<v3s16, MapBlock*>::Iterator
-                               i = changed_blocks.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       lighting_update_blocks.insert(i.getNode()->getKey(),
-                                       i.getNode()->getValue());
+               
+               // Center blocks
+               for(s16 x=blockpos_min.X-extra_borders.X;
+                               x<=blockpos_max.X+extra_borders.X; x++)
+               for(s16 z=blockpos_min.Z-extra_borders.Z;
+                               z<=blockpos_max.Z+extra_borders.Z; z++)
+               for(s16 y=blockpos_min.Y-extra_borders.Y;
+                               y<=blockpos_max.Y+extra_borders.Y; y++)
+               {
+                       v3s16 p(x, y, z);
+                       MapBlock *block = getBlockNoCreateNoEx(p);
+                       assert(block);
+                       lighting_update_blocks.insert(block->getPos(), block);
                }
-               /*// Also force-add all the upmost blocks for proper sunlight
-               for(s16 x=-1; x<=1; x++)
-               for(s16 z=-1; z<=1; z++)
-               {
-                       v3s16 p = block->getPos()+v3s16(x,1,z);
-                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
-               }*/
-#endif
+
                updateLighting(lighting_update_blocks, changed_blocks);
+#endif
                
                /*
                        Set lighting to non-expired state in all of them.
                        This is cheating, but it is not fast enough if all of them
                        would actually be updated.
                */
-               for(s16 x=-1; x<=1; x++)
-               for(s16 y=-1; y<=1; y++)
-               for(s16 z=-1; z<=1; z++)
-               {
-                       v3s16 p = block->getPos()+v3s16(x,y,z);
+               for(s16 x=blockpos_min.X-extra_borders.X;
+                               x<=blockpos_max.X+extra_borders.X; x++)
+               for(s16 z=blockpos_min.Z-extra_borders.Z;
+                               z<=blockpos_max.Z+extra_borders.Z; z++)
+               for(s16 y=blockpos_min.Y-extra_borders.Y;
+                               y<=blockpos_max.Y+extra_borders.Y; y++)
+               {
+                       v3s16 p(x, y, z);
                        getBlockNoCreateNoEx(p)->setLightingExpired(false);
                }
 
+#if 0
                if(enable_mapgen_debug_info == false)
                        t.stop(true); // Hide output
+#endif
        }
 
-       /*
-               Add random objects to block
-       */
-       mapgen::add_random_objects(block);
-
        /*
                Go through changed blocks
        */
@@ -2291,18 +2365,26 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                /*
                        Update day/night difference cache of the MapBlocks
                */
-               block->updateDayNightDiff();
+               block->expireDayNightDiff();
                /*
                        Set block as modified
                */
                block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                               "finishBlockMake updateDayNightDiff");
+                               "finishBlockMake expireDayNightDiff");
        }
 
        /*
-               Set central block as generated
+               Set central blocks as generated
        */
-       block->setGenerated(true);
+       for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
+       for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
+       for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
+       {
+               v3s16 p(x, y, z);
+               MapBlock *block = getBlockNoCreateNoEx(p);
+               assert(block);
+               block->setGenerated(true);
+       }
        
        /*
                Save changed parts of map
@@ -2310,19 +2392,23 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
        */
        //save(MOD_STATE_WRITE_AT_UNLOAD);
 
-       /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
-                       <<blockpos.Z<<")"<<std::endl;*/
+       /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
+                       <<","<<blockpos_requested.Y<<","
+                       <<blockpos_requested.Z<<")"<<std::endl;*/
 #if 0
        if(enable_mapgen_debug_info)
        {
                /*
                        Analyze resulting blocks
                */
-               for(s16 x=-1; x<=1; x++)
-               for(s16 y=-1; y<=1; y++)
-               for(s16 z=-1; z<=1; z++)
-               {
-                       v3s16 p = block->getPos()+v3s16(x,y,z);
+               /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
+               for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
+               for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
+               for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
+               for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
+               for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
+               {
+                       v3s16 p = v3s16(x,y,z);
                        MapBlock *block = getBlockNoCreateNoEx(p);
                        char spos[20];
                        snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
@@ -2332,6 +2418,9 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
        }
 #endif
 
+       MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
+       assert(block);
+
        return block;
 }
 
@@ -3398,14 +3487,20 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
        }
        catch(SerializationError &e)
        {
-               infostream<<"WARNING: Invalid block data in database "
-                               <<" (SerializationError). "
-                               <<"what()="<<e.what()
-                               <<std::endl;
-                               //" Ignoring. A new one will be generated.
-               assert(0);
+               errorstream<<"Invalid block data in database"
+                               <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
+                               <<" (SerializationError): "<<e.what()<<std::endl;
+               
+               // TODO: Block should be marked as invalid in memory so that it is
+               // not touched but the game can run
 
-               // TODO: Copy to a backup database.
+               if(g_settings->getBool("ignore_world_load_errors")){
+                       errorstream<<"Ignoring block load error. Duck and cover! "
+                                       <<"(ignore_world_load_errors)"<<std::endl;
+               } else {
+                       throw SerializationError("Invalid block data in database");
+                       //assert(0);
+               }
        }
 }
 
@@ -3477,15 +3572,15 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
                }
                catch(InvalidFilenameException &e)
                {
-                       return false;
+                       return NULL;
                }
                catch(FileNotGoodException &e)
                {
-                       return false;
+                       return NULL;
                }
                catch(std::exception &e)
                {
-                       return false;
+                       return NULL;
                }
        }