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.
*/
#include "mapblock.h"
#include "main.h"
#include "filesys.h"
-#include "utility.h"
#include "voxel.h"
#include "porting.h"
#include "mapgen.h"
#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<<")"
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
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,
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)
{
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);
// 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,
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--){
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
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);
+ }
+
/*
Add neighboring liquid nodes and the node itself if it is
liquid (=water node was added) to transform queue.
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);
}
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=("
// 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.
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)
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);
}
// 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)
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);
+ }
+
/*
Add neighboring liquid nodes and this node to transform queue.
(it's vital for the node itself to get updated last.)
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);
}
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++;
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) {
<<std::endl;
return NULL;
}
- NodeMetadata *meta = block->m_node_metadata->get(p_rel);
+ NodeMetadata *meta = block->m_node_metadata.get(p_rel);
return 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)
<<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);
}
/*
}
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);
+ }
}
}
}
catch(InvalidFilenameException &e)
{
- return false;
+ return NULL;
}
catch(FileNotGoodException &e)
{
- return false;
+ return NULL;
}
catch(std::exception &e)
{
- return false;
+ return NULL;
}
}