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 "mapsector.h"
#include "mapblock.h"
#include "main.h"
-#ifndef SERVER
-#include "client.h"
-#endif
#include "filesys.h"
-#include "utility.h"
#include "voxel.h"
#include "porting.h"
#include "mapgen.h"
#include "nodemetadata.h"
-#ifndef SERVER
-#include <IMaterialRenderer.h>
-#endif
#include "settings.h"
#include "log.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<<")"
<<" while trying to replace \""
<<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
<<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
- debug_stacks_print_to(errorstream);
+ debug_stacks_print_to(infostream);
return;
}
block->setNodeNoCheck(relpos, n);
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++)
break;
v3s16 pos = block->getPos();
+ v3s16 posnodes = block->getPosRelative();
modified_blocks.insert(pos, block);
blocks_to_update.insert(pos, block);
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);
}
}
{
bool bottom_valid = block->propagateSunlight(light_sources);
+ if(!bottom_valid)
+ num_bottom_invalid++;
+
// If bottom is valid, we're done.
if(bottom_valid)
break;
}
}
-
+
+ }
+
/*
Enable this to disable proper lighting for speeding up map
generation for testing or whatever
}
#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++)
for(s16 y=-1; y<=1; y++)
for(s16 x=-1; x<=1; x++)
{
- v3s16 p(x,y,z);
- MapBlock *block = getBlockNoCreateNoEx(p);
+ v3s16 p2 = p + v3s16(x,y,z);
+ MapBlock *block = getBlockNoCreateNoEx(p2);
if(block == NULL)
continue;
if(block->isDummy())
continue;
if(block->getLightingExpired())
continue;
- vmanip.initialEmerge(p, p);
+ vmanip.initialEmerge(p2, p2);
}*/
// Lighting of block will be updated completely
block->setLightingExpired(false);
}
+ }
{
//TimeTaker timer("unSpreadLight");
/*infostream<<"emerge_time="<<emerge_time<<std::endl;
emerge_time = 0;*/
}
+#endif
//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
}
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
- block->updateDayNightDiff();
+ block->expireDayNightDiff();
}
}
/*
*/
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
- core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
+ 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);
- meta->setOwner(player_name);
- 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
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);
}
/*
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)
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);
}
/*
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);
}
bool succeeded = true;
try{
core::map<v3s16, MapBlock*> modified_blocks;
- std::string st = std::string("");
- addNodeAndUpdate(p, n, modified_blocks, st);
+ addNodeAndUpdate(p, n, modified_blocks);
// Copy modified_blocks to event
for(core::map<v3s16, MapBlock*>::Iterator
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){}
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){}
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){}
MapBlock *block = (*i);
block->incrementUsageTimer(dtime);
-
- if(block->getUsageTimer() > unload_timeout)
+
+ if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
{
v3s16 p = block->getPos();
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++;
if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
neutrals[num_neutrals++] = nb;
} else {
- sources[num_sources++] = nb;
+ // Do not count bottom source, it will screw things up
+ if(dirs[i].Y != -1)
+ sources[num_sources++] = nb;
}
break;
case LIQUID_FLOWING:
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);
}
/*
m_database_read(NULL),
m_database_write(NULL)
{
- infostream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<__FUNCTION_NAME<<std::endl;
//m_chunksize = 8; // Takes a few seconds
// If directory is empty, it is safe to save into it.
if(fs::GetDirListing(m_savedir).size() == 0)
{
- infostream<<"Server: Empty save directory is valid."
+ infostream<<"ServerMap: Empty save directory is valid."
<<std::endl;
m_map_saving_enabled = true;
}
//m_chunksize = 0;
}
- /*try{
- // Load chunk metadata
- loadChunkMeta();
- }
- catch(FileNotGoodException &e){
- infostream<<"WARNING: Could not load chunk metadata."
- <<" Disabling chunk-based generator."
- <<std::endl;
- m_chunksize = 0;
- }*/
-
- /*infostream<<"Server: Successfully loaded chunk "
- "metadata and sector (0,0) from "<<savedir<<
- ", assuming valid save directory."
- <<std::endl;*/
-
- infostream<<"Server: Successfully loaded map "
- <<"and chunk metadata from "<<savedir
+ infostream<<"ServerMap: Successfully loaded map "
+ <<"metadata from "<<savedir
<<", assuming valid save directory."
+ <<" seed="<<m_seed<<"."
<<std::endl;
m_map_saving_enabled = true;
}
catch(std::exception &e)
{
- infostream<<"WARNING: Server: Failed to load map from "<<savedir
+ infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
<<", exception: "<<e.what()<<std::endl;
infostream<<"Please remove the map or fix it."<<std::endl;
infostream<<"WARNING: Map saving will be disabled."<<std::endl;
emergeSector(v2s16(0,0));
// Initially write whole map
- save(false);
+ save(MOD_STATE_CLEAN);
}
ServerMap::~ServerMap()
{
- infostream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<__FUNCTION_NAME<<std::endl;
try
{
if(m_map_saving_enabled)
{
// Save only changed parts
- save(true);
- infostream<<"Server: saved map to "<<m_savedir<<std::endl;
+ save(MOD_STATE_WRITE_AT_UNLOAD);
+ infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
}
else
{
- infostream<<"Server: map not saved"<<std::endl;
+ infostream<<"ServerMap: Map not saved"<<std::endl;
}
}
catch(std::exception &e)
{
- infostream<<"Server: Failed to save map to "<<m_savedir
+ infostream<<"ServerMap: Failed to save map to "<<m_savedir
<<", exception: "<<e.what()<<std::endl;
}
{
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;
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();
/*
{
//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);
*/
// 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);
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)
{
/*infostream<<"Resulting vmanip:"<<std::endl;
data->vmanip.print(infostream);*/
-
+
+ // Make sure affected blocks are loaded
+ 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);
+ }
+
/*
Blit generated stuff to map
NOTE: blitBackAll adds nearly everything to changed_blocks
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
*/
/*
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
NOTE: Will be saved later.
*/
- //save(true);
+ //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);
}
#endif
+ MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
+ assert(block);
+
return block;
}
if(e == SQLITE_ABORT)
throw FileNotGoodException("Could not create database structure");
else
- infostream<<"Server: Database structure was created";
+ infostream<<"ServerMap: Database structure was created";
}
void ServerMap::verifyDatabase() {
throw FileNotGoodException("Cannot prepare read statement");
}
- infostream<<"Server: Database opened"<<std::endl;
+ infostream<<"ServerMap: Database opened"<<std::endl;
}
}
return cc;
}
-void ServerMap::save(bool only_changed)
+void ServerMap::save(ModifiedState save_level)
{
DSTACK(__FUNCTION_NAME);
if(m_map_saving_enabled == false)
return;
}
- if(only_changed == false)
+ if(save_level == MOD_STATE_CLEAN)
infostream<<"ServerMap: Saving whole map, this can take time."
<<std::endl;
- if(only_changed == false || m_map_metadata_changed)
+ if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
{
saveMapMeta();
}
ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
assert(sector->getId() == MAPSECTOR_SERVER);
- if(sector->differs_from_disk || only_changed == false)
+ if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
{
saveSectorMeta(sector);
sector_meta_count++;
block_count_all++;
- if(block->getModified() >= MOD_STATE_WRITE_NEEDED
- || only_changed == false)
+ if(block->getModified() >= save_level)
{
// Lazy beginSave()
if(!save_started){
/*
Only print if something happened or saved whole map
*/
- if(only_changed == false || sector_meta_count != 0
+ if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
|| block_count != 0)
{
infostream<<"ServerMap: Written: "
{
DSTACK(__FUNCTION_NAME);
- infostream<<"ServerMap::saveMapMeta(): "
+ /*infostream<<"ServerMap::saveMapMeta(): "
<<"seed="<<m_seed
- <<std::endl;
+ <<std::endl;*/
createDirs(m_savedir);
{
DSTACK(__FUNCTION_NAME);
- infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
- <<std::endl;
+ /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
+ <<std::endl;*/
std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
m_seed = params.getU64("seed");
- infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
+ verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
}
void ServerMap::saveSectorMeta(ServerMapSector *sector)
o.write((char*)&version, 1);
// Write basic data
- block->serialize(o, version);
-
- // Write extra data stored on disk
- block->serializeDiskExtra(o, version);
+ block->serialize(o, version, true);
// Write block to database
}
// Read basic data
- block->deSerialize(is, version);
+ block->deSerialize(is, version, true);
- // Read extra data stored on disk
- block->deSerializeDiskExtra(is, version);
-
// If it's a new block, insert it to the map
if(created_new)
sector->insertBlock(block);
}
// Read basic data
- block->deSerialize(is, version);
-
- // Read extra data stored on disk
- block->deSerializeDiskExtra(is, version);
+ block->deSerialize(is, version, true);
// If it's a new block, insert it to the map
if(created_new)
}
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;
}
}
out<<"ServerMap: ";
}
-#ifndef SERVER
-
-/*
- ClientMap
-*/
-
-ClientMap::ClientMap(
- Client *client,
- IGameDef *gamedef,
- MapDrawControl &control,
- scene::ISceneNode* parent,
- scene::ISceneManager* mgr,
- s32 id
-):
- Map(dout_client, gamedef),
- scene::ISceneNode(parent, mgr, id),
- m_client(client),
- m_control(control),
- m_camera_position(0,0,0),
- m_camera_direction(0,0,1),
- m_camera_fov(PI)
-{
- m_camera_mutex.Init();
- assert(m_camera_mutex.IsInitialized());
-
- m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
- BS*1000000,BS*1000000,BS*1000000);
-}
-
-ClientMap::~ClientMap()
-{
- /*JMutexAutoLock lock(mesh_mutex);
-
- if(mesh != NULL)
- {
- mesh->drop();
- mesh = NULL;
- }*/
-}
-
-MapSector * ClientMap::emergeSector(v2s16 p2d)
-{
- DSTACK(__FUNCTION_NAME);
- // Check that it doesn't exist already
- try{
- return getSectorNoGenerate(p2d);
- }
- catch(InvalidPositionException &e)
- {
- }
-
- // Create a sector
- ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
-
- {
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
- m_sectors.insert(p2d, sector);
- }
-
- return sector;
-}
-
-#if 0
-void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
-{
- DSTACK(__FUNCTION_NAME);
- ClientMapSector *sector = NULL;
-
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
-
- core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
-
- if(n != NULL)
- {
- sector = (ClientMapSector*)n->getValue();
- assert(sector->getId() == MAPSECTOR_CLIENT);
- }
- else
- {
- sector = new ClientMapSector(this, p2d);
- {
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
- m_sectors.insert(p2d, sector);
- }
- }
-
- sector->deSerialize(is);
-}
-#endif
-
-void ClientMap::OnRegisterSceneNode()
-{
- if(IsVisible)
- {
- SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
- SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
- }
-
- ISceneNode::OnRegisterSceneNode();
-}
-
-static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
- float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
-{
- float d0 = (float)BS * p0.getDistanceFrom(p1);
- v3s16 u0 = p1 - p0;
- v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
- uf.normalize();
- v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
- u32 count = 0;
- for(float s=start_off; s<d0+end_off; s+=step){
- v3f pf = p0f + uf * s;
- v3s16 p = floatToInt(pf, BS);
- MapNode n = map->getNodeNoEx(p);
- bool is_transparent = false;
- const ContentFeatures &f = nodemgr->get(n);
- if(f.solidness == 0)
- is_transparent = (f.visual_solidness != 2);
- else
- is_transparent = (f.solidness != 2);
- if(!is_transparent){
- count++;
- if(count >= needed_count)
- return true;
- }
- step *= stepfac;
- }
- return false;
-}
-
-void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
-{
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
- //m_dout<<DTIME<<"Rendering map..."<<std::endl;
- DSTACK(__FUNCTION_NAME);
-
- bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
-
- std::string prefix;
- if(pass == scene::ESNRP_SOLID)
- prefix = "CM: solid: ";
- else
- prefix = "CM: transparent: ";
-
- /*
- This is called two times per frame, reset on the non-transparent one
- */
- if(pass == scene::ESNRP_SOLID)
- {
- m_last_drawn_sectors.clear();
- }
-
- /*
- Get time for measuring timeout.
-
- Measuring time is very useful for long delays when the
- machine is swapping a lot.
- */
- int time1 = time(0);
-
- //u32 daynight_ratio = m_client->getDayNightRatio();
-
- m_camera_mutex.Lock();
- v3f camera_position = m_camera_position;
- v3f camera_direction = m_camera_direction;
- f32 camera_fov = m_camera_fov;
- m_camera_mutex.Unlock();
-
- /*
- Get all blocks and draw all visible ones
- */
-
- v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
-
- v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
-
- v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
- v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
-
- // Take a fair amount as we will be dropping more out later
- // Umm... these additions are a bit strange but they are needed.
- v3s16 p_blocks_min(
- p_nodes_min.X / MAP_BLOCKSIZE - 3,
- p_nodes_min.Y / MAP_BLOCKSIZE - 3,
- p_nodes_min.Z / MAP_BLOCKSIZE - 3);
- v3s16 p_blocks_max(
- p_nodes_max.X / MAP_BLOCKSIZE + 1,
- p_nodes_max.Y / MAP_BLOCKSIZE + 1,
- p_nodes_max.Z / MAP_BLOCKSIZE + 1);
-
- u32 vertex_count = 0;
- u32 meshbuffer_count = 0;
-
- // For limiting number of mesh updates per frame
- u32 mesh_update_count = 0;
-
- // Number of blocks in rendering range
- u32 blocks_in_range = 0;
- // Number of blocks occlusion culled
- u32 blocks_occlusion_culled = 0;
- // Number of blocks in rendering range but don't have a mesh
- u32 blocks_in_range_without_mesh = 0;
- // Blocks that had mesh that would have been drawn according to
- // rendering range (if max blocks limit didn't kick in)
- u32 blocks_would_have_drawn = 0;
- // Blocks that were drawn and had a mesh
- u32 blocks_drawn = 0;
- // Blocks which had a corresponding meshbuffer for this pass
- u32 blocks_had_pass_meshbuf = 0;
- // Blocks from which stuff was actually drawn
- u32 blocks_without_stuff = 0;
-
- /*
- Collect a set of blocks for drawing
- */
-
- core::map<v3s16, MapBlock*> drawset;
-
- {
- ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
-
- for(core::map<v2s16, MapSector*>::Iterator
- si = m_sectors.getIterator();
- si.atEnd() == false; si++)
- {
- MapSector *sector = si.getNode()->getValue();
- v2s16 sp = sector->getPos();
-
- if(m_control.range_all == false)
- {
- if(sp.X < p_blocks_min.X
- || sp.X > p_blocks_max.X
- || sp.Y < p_blocks_min.Z
- || sp.Y > p_blocks_max.Z)
- continue;
- }
-
- core::list< MapBlock * > sectorblocks;
- sector->getBlocks(sectorblocks);
-
- /*
- Loop through blocks in sector
- */
-
- u32 sector_blocks_drawn = 0;
-
- core::list< MapBlock * >::Iterator i;
- for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
- {
- MapBlock *block = *i;
-
- /*
- Compare block position to camera position, skip
- if not seen on display
- */
-
- float range = 100000 * BS;
- if(m_control.range_all == false)
- range = m_control.wanted_range * BS;
-
- float d = 0.0;
- if(isBlockInSight(block->getPos(), camera_position,
- camera_direction, camera_fov,
- range, &d) == false)
- {
- continue;
- }
-
- // This is ugly (spherical distance limit?)
- /*if(m_control.range_all == false &&
- d - 0.5*BS*MAP_BLOCKSIZE > range)
- continue;*/
-
- blocks_in_range++;
-
-#if 1
- /*
- Update expired mesh (used for day/night change)
-
- It doesn't work exactly like it should now with the
- tasked mesh update but whatever.
- */
-
- bool mesh_expired = false;
-
- {
- JMutexAutoLock lock(block->mesh_mutex);
-
- mesh_expired = block->getMeshExpired();
-
- // Mesh has not been expired and there is no mesh:
- // block has no content
- if(block->mesh == NULL && mesh_expired == false){
- blocks_in_range_without_mesh++;
- continue;
- }
- }
-
- f32 faraway = BS*50;
- //f32 faraway = m_control.wanted_range * BS;
-
- /*
- This has to be done with the mesh_mutex unlocked
- */
- // Pretty random but this should work somewhat nicely
- if(mesh_expired && (
- (mesh_update_count < 3
- && (d < faraway || mesh_update_count < 2)
- )
- ||
- (m_control.range_all && mesh_update_count < 20)
- )
- )
- /*if(mesh_expired && mesh_update_count < 6
- && (d < faraway || mesh_update_count < 3))*/
- {
- mesh_update_count++;
-
- // Mesh has been expired: generate new mesh
- //block->updateMesh(daynight_ratio);
- m_client->addUpdateMeshTask(block->getPos());
-
- mesh_expired = false;
- }
-#endif
-
- /*
- Occlusion culling
- */
-
- v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
- cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
- float step = BS*1;
- float stepfac = 1.1;
- float startoff = BS*1;
- float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
- v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
- s16 bs2 = MAP_BLOCKSIZE/2 + 1;
- u32 needed_count = 1;
- if(
- isOccluded(this, spn, cpn + v3s16(0,0,0),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr) &&
- isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
- step, stepfac, startoff, endoff, needed_count, nodemgr)
- )
- {
- blocks_occlusion_culled++;
- continue;
- }
-
- // This block is in range. Reset usage timer.
- block->resetUsageTimer();
-
- /*
- Ignore if mesh doesn't exist
- */
- {
- JMutexAutoLock lock(block->mesh_mutex);
-
- scene::SMesh *mesh = block->mesh;
-
- if(mesh == NULL){
- blocks_in_range_without_mesh++;
- continue;
- }
- }
-
- // Limit block count in case of a sudden increase
- blocks_would_have_drawn++;
- if(blocks_drawn >= m_control.wanted_max_blocks
- && m_control.range_all == false
- && d > m_control.wanted_min_range * BS)
- continue;
-
- // Add to set
- drawset[block->getPos()] = block;
-
- sector_blocks_drawn++;
- blocks_drawn++;
-
- } // foreach sectorblocks
-
- if(sector_blocks_drawn != 0)
- m_last_drawn_sectors[sp] = true;
- }
- } // ScopeProfiler
-
- /*
- Draw the selected MapBlocks
- */
-
- {
- ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
-
- int timecheck_counter = 0;
- for(core::map<v3s16, MapBlock*>::Iterator
- i = drawset.getIterator();
- i.atEnd() == false; i++)
- {
- {
- timecheck_counter++;
- if(timecheck_counter > 50)
- {
- timecheck_counter = 0;
- int time2 = time(0);
- if(time2 > time1 + 4)
- {
- infostream<<"ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- <<std::endl;
- return;
- }
- }
- }
-
- MapBlock *block = i.getNode()->getValue();
-
- /*
- Draw the faces of the block
- */
- {
- JMutexAutoLock lock(block->mesh_mutex);
-
- scene::SMesh *mesh = block->mesh;
- assert(mesh);
-
- u32 c = mesh->getMeshBufferCount();
- bool stuff_actually_drawn = false;
- for(u32 i=0; i<c; i++)
- {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
- const video::SMaterial& material = buf->getMaterial();
- video::IMaterialRenderer* rnd =
- driver->getMaterialRenderer(material.MaterialType);
- bool transparent = (rnd && rnd->isTransparent());
- // Render transparent on transparent pass and likewise.
- if(transparent == is_transparent_pass)
- {
- if(buf->getVertexCount() == 0)
- errorstream<<"Block ["<<analyze_block(block)
- <<"] contains an empty meshbuf"<<std::endl;
- /*
- This *shouldn't* hurt too much because Irrlicht
- doesn't change opengl textures if the old
- material has the same texture.
- */
- driver->setMaterial(buf->getMaterial());
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- meshbuffer_count++;
- stuff_actually_drawn = true;
- }
- }
- if(stuff_actually_drawn)
- blocks_had_pass_meshbuf++;
- else
- blocks_without_stuff++;
- }
- }
- } // ScopeProfiler
-
- // Log only on solid pass because values are the same
- if(pass == scene::ESNRP_SOLID){
- g_profiler->avg("CM: blocks in range", blocks_in_range);
- g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
- if(blocks_in_range != 0)
- g_profiler->avg("CM: blocks in range without mesh (frac)",
- (float)blocks_in_range_without_mesh/blocks_in_range);
- g_profiler->avg("CM: blocks drawn", blocks_drawn);
- }
-
- g_profiler->avg(prefix+"vertices drawn", vertex_count);
- if(blocks_had_pass_meshbuf != 0)
- g_profiler->avg(prefix+"meshbuffers per block",
- (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
- if(blocks_drawn != 0)
- g_profiler->avg(prefix+"empty blocks (frac)",
- (float)blocks_without_stuff / blocks_drawn);
-
- m_control.blocks_drawn = blocks_drawn;
- m_control.blocks_would_have_drawn = blocks_would_have_drawn;
-
- /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
- <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
-}
-
-void ClientMap::renderPostFx()
-{
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
- // Sadly ISceneManager has no "post effects" render pass, in that case we
- // could just register for that and handle it in renderMap().
-
- m_camera_mutex.Lock();
- v3f camera_position = m_camera_position;
- m_camera_mutex.Unlock();
-
- MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
-
- // - If the player is in a solid node, make everything black.
- // - If the player is in liquid, draw a semi-transparent overlay.
- const ContentFeatures& features = nodemgr->get(n);
- video::SColor post_effect_color = features.post_effect_color;
- if(features.solidness == 2 && g_settings->getBool("free_move") == false)
- {
- post_effect_color = video::SColor(255, 0, 0, 0);
- }
- if (post_effect_color.getAlpha() != 0)
- {
- // Draw a full-screen rectangle
- video::IVideoDriver* driver = SceneManager->getVideoDriver();
- v2u32 ss = driver->getScreenSize();
- core::rect<s32> rect(0,0, ss.X, ss.Y);
- driver->draw2DRectangle(post_effect_color, rect);
- }
-}
-
-bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
- core::map<v3s16, MapBlock*> *affected_blocks)
-{
- bool changed = false;
- /*
- Add it to all blocks touching it
- */
- v3s16 dirs[7] = {
- v3s16(0,0,0), // this
- v3s16(0,0,1), // back
- v3s16(0,1,0), // top
- v3s16(1,0,0), // right
- v3s16(0,0,-1), // front
- v3s16(0,-1,0), // bottom
- v3s16(-1,0,0), // left
- };
- for(u16 i=0; i<7; i++)
- {
- v3s16 p2 = p + dirs[i];
- // Block position of neighbor (or requested) node
- v3s16 blockpos = getNodeBlockPos(p2);
- MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
- if(blockref == NULL)
- continue;
- // Relative position of requested node
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- if(blockref->setTempMod(relpos, mod))
- {
- changed = true;
- }
- }
- if(changed && affected_blocks!=NULL)
- {
- for(u16 i=0; i<7; i++)
- {
- v3s16 p2 = p + dirs[i];
- // Block position of neighbor (or requested) node
- v3s16 blockpos = getNodeBlockPos(p2);
- MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
- if(blockref == NULL)
- continue;
- affected_blocks->insert(blockpos, blockref);
- }
- }
- return changed;
-}
-
-bool ClientMap::clearTempMod(v3s16 p,
- core::map<v3s16, MapBlock*> *affected_blocks)
-{
- bool changed = false;
- v3s16 dirs[7] = {
- v3s16(0,0,0), // this
- v3s16(0,0,1), // back
- v3s16(0,1,0), // top
- v3s16(1,0,0), // right
- v3s16(0,0,-1), // front
- v3s16(0,-1,0), // bottom
- v3s16(-1,0,0), // left
- };
- for(u16 i=0; i<7; i++)
- {
- v3s16 p2 = p + dirs[i];
- // Block position of neighbor (or requested) node
- v3s16 blockpos = getNodeBlockPos(p2);
- MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
- if(blockref == NULL)
- continue;
- // Relative position of requested node
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- if(blockref->clearTempMod(relpos))
- {
- changed = true;
- }
- }
- if(changed && affected_blocks!=NULL)
- {
- for(u16 i=0; i<7; i++)
- {
- v3s16 p2 = p + dirs[i];
- // Block position of neighbor (or requested) node
- v3s16 blockpos = getNodeBlockPos(p2);
- MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
- if(blockref == NULL)
- continue;
- affected_blocks->insert(blockpos, blockref);
- }
- }
- return changed;
-}
-
-void ClientMap::expireMeshes(bool only_daynight_diffed)
-{
- TimeTaker timer("expireMeshes()");
-
- core::map<v2s16, MapSector*>::Iterator si;
- si = m_sectors.getIterator();
- for(; si.atEnd() == false; si++)
- {
- 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;
-
- if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
- {
- continue;
- }
-
- {
- JMutexAutoLock lock(block->mesh_mutex);
- if(block->mesh != NULL)
- {
- /*block->mesh->drop();
- block->mesh = NULL;*/
- block->setMeshExpired(true);
- }
- }
- }
- }
-}
-
-void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
-{
- assert(mapType() == MAPTYPE_CLIENT);
-
- try{
- v3s16 p = blockpos + v3s16(0,0,0);
- MapBlock *b = getBlockNoCreate(p);
- b->updateMesh(daynight_ratio);
- //b->setMeshExpired(true);
- }
- catch(InvalidPositionException &e){}
- // Leading edge
- try{
- v3s16 p = blockpos + v3s16(-1,0,0);
- MapBlock *b = getBlockNoCreate(p);
- b->updateMesh(daynight_ratio);
- //b->setMeshExpired(true);
- }
- catch(InvalidPositionException &e){}
- try{
- v3s16 p = blockpos + v3s16(0,-1,0);
- MapBlock *b = getBlockNoCreate(p);
- b->updateMesh(daynight_ratio);
- //b->setMeshExpired(true);
- }
- catch(InvalidPositionException &e){}
- try{
- v3s16 p = blockpos + v3s16(0,0,-1);
- MapBlock *b = getBlockNoCreate(p);
- b->updateMesh(daynight_ratio);
- //b->setMeshExpired(true);
- }
- catch(InvalidPositionException &e){}
-}
-
-#if 0
-/*
- Update mesh of block in which the node is, and if the node is at the
- leading edge, update the appropriate leading blocks too.
-*/
-void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
-{
- v3s16 dirs[4] = {
- v3s16(0,0,0),
- v3s16(-1,0,0),
- v3s16(0,-1,0),
- v3s16(0,0,-1),
- };
- v3s16 blockposes[4];
- for(u32 i=0; i<4; i++)
- {
- v3s16 np = nodepos + dirs[i];
- blockposes[i] = getNodeBlockPos(np);
- // Don't update mesh of block if it has been done already
- bool already_updated = false;
- for(u32 j=0; j<i; j++)
- {
- if(blockposes[j] == blockposes[i])
- {
- already_updated = true;
- break;
- }
- }
- if(already_updated)
- continue;
- // Update mesh
- MapBlock *b = getBlockNoCreate(blockposes[i]);
- b->updateMesh(daynight_ratio);
- }
-}
-#endif
-
-void ClientMap::PrintInfo(std::ostream &out)
-{
- out<<"ClientMap: ";
-}
-
-#endif // !SERVER
-
/*
MapVoxelManipulator
*/