#include "mapsector.h"
#include "mapblock.h"
#include "main.h"
-#include "client.h"
#include "filesys.h"
#include "utility.h"
#include "voxel.h"
#include "porting.h"
#include "mapgen.h"
#include "nodemetadata.h"
+#include "settings.h"
+#include "log.h"
+#include "profiler.h"
+#include "nodedef.h"
+#include "gamedef.h"
+
+#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
-extern "C" {
- #include "sqlite3.h"
-}
/*
SQLite format specification:
- Initially only replaces sectors/ and sectors2/
+
+ If map.sqlite does not exist in the save dir
+ or the block was not found in the database
+ the map will try to load from sectors folder.
+ In either case, map.sqlite will be created
+ and all future saves will save there.
+
+ Structure of map.sqlite:
+ Tables:
+ blocks
+ (PK) INT pos
+ BLOB data
*/
/*
Map
*/
-Map::Map(std::ostream &dout):
+Map::Map(std::ostream &dout, IGameDef *gamedef):
m_dout(dout),
+ m_gamedef(gamedef),
m_sector_cache(NULL)
{
/*m_sector_mutex.Init();
v3s16 blockpos = getNodeBlockPos(p);
MapBlock *block = getBlockNoCreate(blockpos);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+ // Never allow placing CONTENT_IGNORE, it fucks up stuff
+ if(n.getContent() == CONTENT_IGNORE){
+ errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
+ <<" while trying to replace \""
+ <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
+ <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
+ debug_stacks_print_to(infostream);
+ return;
+ }
block->setNodeNoCheck(relpos, n);
}
core::map<v3s16, bool> & light_sources,
core::map<v3s16, MapBlock*> & modified_blocks)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)
*/
- if(n2.getLight(bank) < oldlight)
+ if(n2.getLight(bank, nodemgr) < oldlight)
{
/*
And the neighbor is transparent and it has some light
*/
- if(n2.light_propagates() && n2.getLight(bank) != 0)
+ if(nodemgr->get(n2).light_propagates
+ && n2.getLight(bank, nodemgr) != 0)
{
/*
Set light to 0 and add to queue
*/
- u8 current_light = n2.getLight(bank);
- n2.setLight(bank, 0);
+ u8 current_light = n2.getLight(bank, nodemgr);
+ n2.setLight(bank, 0, nodemgr);
block->setNode(relpos, n2);
unlighted_nodes.insert(n2pos, current_light);
*/
/*if(light_sources.find(n2pos))
{
- std::cout<<"Removed from light_sources"<<std::endl;
+ infostream<<"Removed from light_sources"<<std::endl;
light_sources.remove(n2pos);
}*/
}
}
}
- /*dstream<<"unspreadLight(): Changed block "
+ /*infostream<<"unspreadLight(): Changed block "
<<blockchangecount<<" times"
<<" for "<<from_nodes.size()<<" nodes"
<<std::endl;*/
core::map<v3s16, bool> & from_nodes,
core::map<v3s16, MapBlock*> & modified_blocks)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
const v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
{
v3s16 pos = j.getNode()->getKey();
//v3s16 pos = *j;
- //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
+ //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
v3s16 blockpos = getNodeBlockPos(pos);
// Only fetch a new block if the block position has changed
// Get node straight from the block
MapNode n = block->getNode(relpos);
- u8 oldlight = n.getLight(bank);
+ u8 oldlight = n.getLight(bank, nodemgr);
u8 newlight = diminish_light(oldlight);
// Loop through 6 neighbors
If the neighbor is brighter than the current node,
add to list (it will light up this node on its turn)
*/
- if(n2.getLight(bank) > undiminish_light(oldlight))
+ if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
{
lighted_nodes.insert(n2pos, true);
//lighted_nodes.push_back(n2pos);
If the neighbor is dimmer than how much light this node
would spread on it, add to list
*/
- if(n2.getLight(bank) < newlight)
+ if(n2.getLight(bank, nodemgr) < newlight)
{
- if(n2.light_propagates())
+ if(nodemgr->get(n2).light_propagates)
{
- n2.setLight(bank, newlight);
+ n2.setLight(bank, newlight, nodemgr);
block->setNode(relpos, n2);
lighted_nodes.insert(n2pos, true);
//lighted_nodes.push_back(n2pos);
}
}
- /*dstream<<"spreadLight(): Changed block "
+ /*infostream<<"spreadLight(): Changed block "
<<blockchangecount<<" times"
<<" for "<<from_nodes.size()<<" nodes"
<<std::endl;*/
v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
v3s16 dirs[6] = {
v3s16(0,0,1), // back
v3s16(0,1,0), // top
{
continue;
}
- if(n2.getLight(bank) > brightest_light || found_something == false){
- brightest_light = n2.getLight(bank);
+ if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
+ brightest_light = n2.getLight(bank, nodemgr);
brightest_pos = n2pos;
found_something = true;
}
s16 Map::propagateSunlight(v3s16 start,
core::map<v3s16, MapBlock*> & modified_blocks)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
s16 y = start.Y;
for(; ; y--)
{
v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
MapNode n = block->getNode(relpos);
- if(n.sunlight_propagates())
+ if(nodemgr->get(n).sunlight_propagates)
{
- n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+ n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
block->setNode(relpos, n);
modified_blocks.insert(blockpos, block);
}
else
{
- /*// Turn mud into grass
- if(n.getContent() == CONTENT_MUD)
- {
- n.setContent(CONTENT_GRASS);
- block->setNode(relpos, n);
- modified_blocks.insert(blockpos, block);
- }*/
-
// Sunlight goes no further
break;
}
core::map<v3s16, MapBlock*> & a_blocks,
core::map<v3s16, MapBlock*> & modified_blocks)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
/*m_dout<<DTIME<<"Map::updateLighting(): "
<<a_blocks.size()<<" blocks."<<std::endl;*/
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));
- u8 oldlight = n.getLight(bank);
- n.setLight(bank, 0);
- block->setNode(v3s16(x,y,z), n);
+ MapNode n = block->getNode(p);
+ u8 oldlight = n.getLight(bank, nodemgr);
+ n.setLight(bank, 0, nodemgr);
+ 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);
}
}
dummy block.
*/
//assert(0);
- dstream<<"updateLighting(): InvalidPositionException"
+ infostream<<"updateLighting(): InvalidPositionException"
<<std::endl;
}
}
{
bool bottom_valid = block->propagateSunlight(light_sources);
+ if(!bottom_valid)
+ num_bottom_invalid++;
+
// If bottom is valid, we're done.
if(bottom_valid)
break;
assert(0);
}
- /*dstream<<"Bottom for sunlight-propagated block ("
+ /*infostream<<"Bottom for sunlight-propagated block ("
<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
<<std::endl;*/
}
}
-
+
+ }
+
/*
Enable this to disable proper lighting for speeding up map
generation for testing or whatever
*/
#if 0
- //if(g_settings.get(""))
+ //if(g_settings->get(""))
{
core::map<v3s16, MapBlock*>::Iterator i;
i = blocks_to_update.getIterator();
}
#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();
- dstream<<"unspreadLight modified "<<diff<<std::endl;
- }
+ 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();
- dstream<<"spreadLight modified "<<diff<<std::endl;
- }
+ 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");
- vmanip.unspreadLight(bank, unlight_from, light_sources);
+ vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
}
{
//TimeTaker timer("spreadLight");
- vmanip.spreadLight(bank, light_sources);
+ vmanip.spreadLight(bank, light_sources, nodemgr);
}
{
//TimeTaker timer("blitBack");
vmanip.blitBack(modified_blocks);
}
- /*dstream<<"emerge_time="<<emerge_time<<std::endl;
+ /*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)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
/*PrintInfo(m_dout);
m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
try{
MapNode topnode = getNode(toppos);
- if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
+ if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
node_under_sunlight = false;
}
catch(InvalidPositionException &e)
{
}
-#if 0
- /*
- If the new node is solid and there is grass below, change it to mud
- */
- if(content_features(n).walkable == true)
- {
- try{
- MapNode bottomnode = getNode(bottompos);
-
- if(bottomnode.getContent() == CONTENT_GRASS
- || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
- {
- bottomnode.setContent(CONTENT_MUD);
- setNode(bottompos, bottomnode);
- }
- }
- catch(InvalidPositionException &e)
- {
- }
- }
-#endif
-
-#if 0
- /*
- If the new node is mud and it is under sunlight, change it
- to grass
- */
- if(n.getContent() == CONTENT_MUD && node_under_sunlight)
- {
- n.setContent(CONTENT_GRASS);
- }
-#endif
-
/*
Remove all light that has come out of this node
*/
{
enum LightBank bank = banks[i];
- u8 lightwas = getNode(p).getLight(bank);
+ u8 lightwas = getNode(p).getLight(bank, nodemgr);
// 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);
+ n.setLight(bank, 0, nodemgr);
}
/*
If node lets sunlight through and is under sunlight, it has
sunlight too.
*/
- if(node_under_sunlight && content_features(n).sunlight_propagates)
+ if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
{
- n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+ n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
}
/*
/*
Add intial metadata
*/
-
- NodeMetadata *meta_proto = content_features(n).initial_metadata;
- if(meta_proto)
- {
- NodeMetadata *meta = meta_proto->clone();
- setNodeMetadata(p, meta);
+
+ 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);
+ }
}
/*
TODO: This could be optimized by mass-unlighting instead
of looping
*/
- if(node_under_sunlight && !content_features(n).sunlight_propagates)
+ if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
{
s16 y = p.Y - 1;
for(;; y--){
break;
}
- if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
+ if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
{
unLightNeighbors(LIGHTBANK_DAY,
- n2pos, n2.getLight(LIGHTBANK_DAY),
+ n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
light_sources, modified_blocks);
- n2.setLight(LIGHTBANK_DAY, 0);
+ n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
setNode(n2pos, n2);
}
else
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
- block->updateDayNightDiff();
+ block->expireDayNightDiff();
}
/*
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
- if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
+ if(nodemgr->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();
+
/*PrintInfo(m_dout);
m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
try{
MapNode topnode = getNode(toppos);
- if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
+ if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != 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),
+ getNode(p).getLight(bank, nodemgr),
light_sources, modified_blocks);
}
// TODO: Is this needed? Lighting is cleared up there already.
try{
MapNode n = getNode(p);
- n.setLight(LIGHTBANK_DAY, 0);
+ n.setLight(LIGHTBANK_DAY, 0, nodemgr);
setNode(p, n);
}
catch(InvalidPositionException &e)
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
- block->updateDayNightDiff();
+ block->expireDayNightDiff();
}
/*
v3s16 p2 = p + dirs[i];
MapNode n2 = getNode(p2);
- if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
+ if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
{
m_transforming_liquid.push_back(p2);
}
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){}
{
bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
+ // Profile modified reasons
+ Profiler modprofiler;
+
core::list<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
+ u32 block_count_all = 0;
core::map<v2s16, MapSector*>::Iterator si;
+ beginSave();
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
core::list<MapBlock*> blocks;
sector->getBlocks(blocks);
+
for(core::list<MapBlock*>::Iterator i = blocks.begin();
i != blocks.end(); i++)
{
if(block->getModified() != MOD_STATE_CLEAN
&& save_before_unloading)
{
+ modprofiler.add(block->getModifiedReason(), 1);
saveBlock(block);
saved_blocks_count++;
}
else
{
all_blocks_deleted = false;
+ block_count_all++;
}
}
sector_deletion_queue.push_back(si.getNode()->getKey());
}
}
+ endSave();
// Finally delete the empty sectors
deleteSectors(sector_deletion_queue);
if(deleted_blocks_count != 0)
{
- PrintInfo(dstream); // ServerMap/ClientMap:
- dstream<<"Unloaded "<<deleted_blocks_count
+ PrintInfo(infostream); // ServerMap/ClientMap:
+ infostream<<"Unloaded "<<deleted_blocks_count
<<" blocks from memory";
if(save_before_unloading)
- dstream<<", of which "<<saved_blocks_count<<" were written";
- dstream<<"."<<std::endl;
+ infostream<<", of which "<<saved_blocks_count<<" were written";
+ infostream<<", "<<block_count_all<<" blocks in memory";
+ infostream<<"."<<std::endl;
+ if(saved_blocks_count != 0){
+ PrintInfo(infostream); // ServerMap/ClientMap:
+ infostream<<"Blocks modified by: "<<std::endl;
+ modprofiler.print(infostream);
+ }
}
}
deleteSectors(sector_deletion_queue);
- dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
+ infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
<<", of which "<<saved_blocks_count<<" were wr."
<<std::endl;
void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
{
+ INodeDefManager *nodemgr = m_gamedef->ndef();
+
DSTACK(__FUNCTION_NAME);
//TimeTaker timer("transformLiquids()");
u32 initial_size = m_transforming_liquid.size();
/*if(initial_size != 0)
- dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
+ infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
// list of nodes that due to viscosity have not reached their max level height
UniqueQueue<v3s16> must_reflow;
*/
s8 liquid_level = -1;
u8 liquid_kind = CONTENT_IGNORE;
- LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
+ LiquidType liquid_type = nodemgr->get(n0).liquid_type;
switch (liquid_type) {
case LIQUID_SOURCE:
liquid_level = LIQUID_LEVEL_SOURCE;
- liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
+ liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
break;
case LIQUID_FLOWING:
liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
}
v3s16 npos = p0 + dirs[i];
NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
- switch (content_features(nb.n.getContent()).liquid_type) {
+ switch (nodemgr->get(nb.n.getContent()).liquid_type) {
case LIQUID_NONE:
if (nb.n.getContent() == CONTENT_AIR) {
airs[num_airs++] = nb;
case LIQUID_SOURCE:
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if (liquid_kind == CONTENT_AIR)
- liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
- if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
+ liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
+ 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:
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if (liquid_kind == CONTENT_AIR)
- liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
- if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
+ liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
+ if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
neutrals[num_neutrals++] = nb;
} else {
flows[num_flows++] = nb;
// 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 = content_features(liquid_kind).liquid_alternative_source;
+ new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
} 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 = LIQUID_LEVEL_MAX;
if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
max_node_level = nb_liquid_level + WATER_DROP_BOOST;
- }
+ } else if (nb_liquid_level > max_node_level)
+ max_node_level = nb_liquid_level;
break;
case NEIGHBOR_LOWER:
break;
}
}
- u8 viscosity = content_features(liquid_kind).liquid_viscosity;
+ u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
if (viscosity > 1 && max_node_level != liquid_level) {
// amount to gain, limited by viscosity
// must be at least 1 in absolute value
/*
check if anything has changed. if not, just continue with the next node.
*/
- if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
+ if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
== flowing_down)))
/*
update the current node
*/
- bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
- if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
+ //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
+ if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
// set level to last 3 bits, flowing down bit to 4th bit
n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
} else {
if(block != NULL) {
modified_blocks.insert(blockpos, block);
// If node emits light, MapBlock requires lighting update
- if(content_features(n0).light_source != 0)
+ if(nodemgr->get(n0).light_source != 0)
lighting_modified_blocks[block->getPos()] = block;
}
/*
enqueue neighbors for update if neccessary
*/
- switch (content_features(n0.getContent()).liquid_type) {
+ switch (nodemgr->get(n0.getContent()).liquid_type) {
case LIQUID_SOURCE:
case LIQUID_FLOWING:
// make sure source flows into all neighboring nodes
break;
}
}
- //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
+ //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
while (must_reflow.size() > 0)
m_transforming_liquid.push_back(must_reflow.pop_front());
updateLighting(lighting_modified_blocks, modified_blocks);
v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos);
- if(block == NULL)
+ if(!block){
+ infostream<<"Map::getNodeMetadata(): Need to emerge "
+ <<PP(blockpos)<<std::endl;
+ block = emergeBlock(blockpos, false);
+ }
+ if(!block)
{
- dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
+ infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
<<std::endl;
return NULL;
}
- NodeMetadata *meta = block->m_node_metadata.get(p_rel);
+ NodeMetadata *meta = block->m_node_metadata->get(p_rel);
return meta;
}
v3s16 blockpos = getNodeBlockPos(p);
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
MapBlock *block = getBlockNoCreateNoEx(blockpos);
- if(block == NULL)
+ if(!block){
+ infostream<<"Map::setNodeMetadata(): Need to emerge "
+ <<PP(blockpos)<<std::endl;
+ block = emergeBlock(blockpos, false);
+ }
+ if(!block)
{
- dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
+ infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
<<std::endl;
return;
}
- block->m_node_metadata.set(p_rel, meta);
+ block->m_node_metadata->set(p_rel, meta);
}
void Map::removeNodeMetadata(v3s16 p)
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if(block == NULL)
{
- dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
+ infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
<<std::endl;
return;
}
- block->m_node_metadata.remove(p_rel);
+ block->m_node_metadata->remove(p_rel);
}
void Map::nodeMetadataStep(float dtime,
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
{
MapBlock *block = *i;
- bool changed = block->m_node_metadata.step(dtime);
+ bool changed = block->m_node_metadata->step(dtime);
if(changed)
changed_blocks[block->getPos()] = block;
}
ServerMap
*/
-ServerMap::ServerMap(std::string savedir):
- Map(dout_server),
+ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
+ Map(dout_server, gamedef),
m_seed(0),
- m_map_metadata_changed(true)
+ m_map_metadata_changed(true),
+ m_database(NULL),
+ m_database_read(NULL),
+ m_database_write(NULL)
{
- dstream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<__FUNCTION_NAME<<std::endl;
//m_chunksize = 8; // Takes a few seconds
- m_seed = (((u64)(myrand()%0xffff)<<0)
- + ((u64)(myrand()%0xffff)<<16)
- + ((u64)(myrand()%0xffff)<<32)
- + ((u64)(myrand()%0xffff)<<48));
+ if (g_settings->get("fixed_map_seed").empty())
+ {
+ m_seed = (((u64)(myrand()%0xffff)<<0)
+ + ((u64)(myrand()%0xffff)<<16)
+ + ((u64)(myrand()%0xffff)<<32)
+ + ((u64)(myrand()%0xffff)<<48));
+ }
+ else
+ {
+ m_seed = g_settings->getU64("fixed_map_seed");
+ }
/*
Experimental and debug stuff
// If directory is empty, it is safe to save into it.
if(fs::GetDirListing(m_savedir).size() == 0)
{
- dstream<<DTIME<<"Server: Empty save directory is valid."
+ infostream<<"ServerMap: Empty save directory is valid."
<<std::endl;
m_map_saving_enabled = true;
}
loadMapMeta();
}
catch(FileNotGoodException &e){
- dstream<<DTIME<<"WARNING: Could not load map metadata"
+ infostream<<"WARNING: Could not load map metadata"
//<<" Disabling chunk-based generator."
<<std::endl;
//m_chunksize = 0;
}
- /*try{
- // Load chunk metadata
- loadChunkMeta();
- }
- catch(FileNotGoodException &e){
- dstream<<DTIME<<"WARNING: Could not load chunk metadata."
- <<" Disabling chunk-based generator."
- <<std::endl;
- m_chunksize = 0;
- }*/
-
- /*dstream<<DTIME<<"Server: Successfully loaded chunk "
- "metadata and sector (0,0) from "<<savedir<<
- ", assuming valid save directory."
- <<std::endl;*/
-
- dstream<<DTIME<<"INFO: 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)
{
- dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
+ infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
<<", exception: "<<e.what()<<std::endl;
- dstream<<"Please remove the map or fix it."<<std::endl;
- dstream<<"WARNING: Map saving will be disabled."<<std::endl;
+ infostream<<"Please remove the map or fix it."<<std::endl;
+ infostream<<"WARNING: Map saving will be disabled."<<std::endl;
}
- dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
+ infostream<<"Initializing new map."<<std::endl;
// Create zero sector
emergeSector(v2s16(0,0));
// Initially write whole map
- save(false);
+ save(MOD_STATE_CLEAN);
}
ServerMap::~ServerMap()
{
- dstream<<__FUNCTION_NAME<<std::endl;
+ verbosestream<<__FUNCTION_NAME<<std::endl;
try
{
if(m_map_saving_enabled)
{
// Save only changed parts
- save(true);
- dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
+ save(MOD_STATE_WRITE_AT_UNLOAD);
+ infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
}
else
{
- dstream<<DTIME<<"Server: map not saved"<<std::endl;
+ infostream<<"ServerMap: Map not saved"<<std::endl;
}
}
catch(std::exception &e)
{
- dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
+ infostream<<"ServerMap: Failed to save map to "<<m_savedir
<<", exception: "<<e.what()<<std::endl;
}
+ /*
+ Close database if it was opened
+ */
+ if(m_database_read)
+ sqlite3_finalize(m_database_read);
+ if(m_database_write)
+ sqlite3_finalize(m_database_write);
+ if(m_database)
+ sqlite3_close(m_database);
+
#if 0
/*
Free all MapChunks
void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
{
- bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+ bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
if(enable_mapgen_debug_info)
- dstream<<"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();
/*
Create the whole area of this and the neighboring blocks
{
//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;
- /*dstream<<"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)
{
- //dstream<<"finishBlockMake(): no-op"<<std::endl;
+ //infostream<<"finishBlockMake(): no-op"<<std::endl;
return NULL;
}
- bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+ bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
+
+ /*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);
+ }
- /*dstream<<"Resulting vmanip:"<<std::endl;
- data->vmanip.print(dstream);*/
-
/*
Blit generated stuff to map
NOTE: blitBackAll adds nearly everything to changed_blocks
}
if(enable_mapgen_debug_info)
- dstream<<"finishBlockMake: changed_blocks.size()="
+ infostream<<"finishBlockMake: changed_blocks.size()="
<<changed_blocks.size()<<std::endl;
/*
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);
+ block->raiseModified(MOD_STATE_WRITE_NEEDED,
+ "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);
- /*dstream<<"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);
- dstream<<"Generated "<<spos<<": "
+ infostream<<"Generated "<<spos<<": "
<<analyze_block(block)<<std::endl;
}
}
#endif
+ MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
+ assert(block);
+
return block;
}
/*
Try to load metadata from disk
*/
+#if 0
if(loadSectorMeta(p2d) == true)
{
ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
if(sector == NULL)
{
- dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
+ infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
throw InvalidPositionException("");
}
return sector;
}
-
+#endif
/*
Do not create over-limit
*/
Generate blank sector
*/
- sector = new ServerMapSector(this, p2d);
+ sector = new ServerMapSector(this, p2d, m_gamedef);
// Sector position on map in nodes
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
{
DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
- /*dstream<<"generateBlock(): "
+ /*infostream<<"generateBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
- bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+ bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
TimeTaker timer("generateBlock");
*/
if(blockpos_over_limit(p))
{
- dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
+ infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
throw InvalidPositionException("generateBlock(): pos. over limit");
}
MapNode n = block->getNode(p);
if(n.getContent() == CONTENT_IGNORE)
{
- dstream<<"CONTENT_IGNORE at "
+ infostream<<"CONTENT_IGNORE at "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;
erroneus_content = true;
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
{
MapNode n;
- if(y0%2==0)
- n.setContent(CONTENT_AIR);
- else
- n.setContent(CONTENT_STONE);
+ n.setContent(CONTENT_AIR);
block->setNode(v3s16(x0,y0,z0), n);
}
}
}
catch(InvalidPositionException &e)
{
- dstream<<"createBlock: createSector() failed"<<std::endl;
+ infostream<<"createBlock: createSector() failed"<<std::endl;
throw e;
}
/*
*/
/*catch(std::exception &e)
{
- dstream<<"createBlock: createSector() failed: "
+ infostream<<"createBlock: createSector() failed: "
<<e.what()<<std::endl;
throw e;
}*/
return NULL;
}
-#if 0
- /*
- Do not generate over-limit
- */
- if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
- throw InvalidPositionException("emergeBlock(): pos. over limit");
-
- v2s16 p2d(p.X, p.Z);
- s16 block_y = p.Y;
- /*
- This will create or load a sector if not found in memory.
- If block exists on disk, it will be loaded.
- */
- ServerMapSector *sector;
- try{
- sector = createSector(p2d);
- //sector = emergeSector(p2d, changed_blocks);
- }
- catch(InvalidPositionException &e)
- {
- dstream<<"emergeBlock: createSector() failed: "
- <<e.what()<<std::endl;
- dstream<<"Path to failed sector: "<<getSectorDir(p2d)
- <<std::endl
- <<"You could try to delete it."<<std::endl;
- throw e;
- }
- catch(VersionMismatchException &e)
- {
- dstream<<"emergeBlock: createSector() failed: "
- <<e.what()<<std::endl;
- dstream<<"Path to failed sector: "<<getSectorDir(p2d)
- <<std::endl
- <<"You could try to delete it."<<std::endl;
- throw e;
- }
-
- /*
- Try to get a block from the sector
- */
-
- bool does_not_exist = false;
- bool lighting_expired = false;
- MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
-
- // If not found, try loading from disk
- if(block == NULL)
- {
- block = loadBlock(p);
- }
-
- // Handle result
- if(block == NULL)
- {
- does_not_exist = true;
- }
- else if(block->isDummy() == true)
- {
- does_not_exist = true;
- }
- else if(block->getLightingExpired())
- {
- lighting_expired = true;
- }
- else
- {
- // Valid block
- //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
- return block;
- }
-
- /*
- If block was not found on disk and not going to generate a
- new one, make sure there is a dummy block in place.
- */
- if(only_from_disk && (does_not_exist || lighting_expired))
- {
- //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
-
- if(block == NULL)
- {
- // Create dummy block
- block = new MapBlock(this, p, true);
-
- // Add block to sector
- sector->insertBlock(block);
- }
- // Done.
- return block;
- }
-
- //dstream<<"Not found on disk, generating."<<std::endl;
- // 0ms
- //TimeTaker("emergeBlock() generate");
-
- //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
-
- /*
- If the block doesn't exist, generate the block.
- */
- if(does_not_exist)
- {
- block = generateBlock(p, block, sector, changed_blocks,
- lighting_invalidated_blocks);
- }
-
- if(lighting_expired)
- {
- lighting_invalidated_blocks.insert(p, block);
- }
-
-#if 0
- /*
- Initially update sunlight
- */
- {
- core::map<v3s16, bool> light_sources;
- bool black_air_left = false;
- bool bottom_invalid =
- block->propagateSunlight(light_sources, true,
- &black_air_left);
-
- // If sunlight didn't reach everywhere and part of block is
- // above ground, lighting has to be properly updated
- //if(black_air_left && some_part_underground)
- if(black_air_left)
- {
- lighting_invalidated_blocks[block->getPos()] = block;
- }
-
- if(bottom_invalid)
- {
- lighting_invalidated_blocks[block->getPos()] = block;
- }
- }
-#endif
-
- return block;
-}
-#endif
-
s16 ServerMap::findGroundLevel(v2s16 p2d)
{
#if 0
//return (s16)level;
}
-void ServerMap::createDirs(std::string path)
-{
- if(fs::CreateAllDirs(path) == false)
- {
- m_dout<<DTIME<<"ServerMap: Failed to create directory "
- <<"\""<<path<<"\""<<std::endl;
- throw BaseException("ServerMap failed to create directory");
- }
+void ServerMap::createDatabase() {
+ int e;
+ assert(m_database);
+ e = sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `blocks` ("
+ "`pos` INT NOT NULL PRIMARY KEY,"
+ "`data` BLOB"
+ ");"
+ , NULL, NULL, NULL);
+ if(e == SQLITE_ABORT)
+ throw FileNotGoodException("Could not create database structure");
+ else
+ infostream<<"ServerMap: Database structure was created";
}
-std::string ServerMap::getSectorDir(v2s16 pos, int layout)
-{
- char cc[9];
- switch(layout)
+void ServerMap::verifyDatabase() {
+ if(m_database)
+ return;
+
{
- case 1:
- snprintf(cc, 9, "%.4x%.4x",
- (unsigned int)pos.X&0xffff,
- (unsigned int)pos.Y&0xffff);
-
- return m_savedir + "/sectors/" + cc;
- case 2:
- snprintf(cc, 9, "%.3x/%.3x",
- (unsigned int)pos.X&0xfff,
- (unsigned int)pos.Y&0xfff);
-
- return m_savedir + "/sectors2/" + cc;
- default:
+ std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
+ bool needs_create = false;
+ int d;
+
+ /*
+ Open the database connection
+ */
+
+ createDirs(m_savedir);
+
+ if(!fs::PathExists(dbp))
+ needs_create = true;
+
+ d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
+ if(d != SQLITE_OK) {
+ infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
+ throw FileNotGoodException("Cannot open database file");
+ }
+
+ if(needs_create)
+ createDatabase();
+
+ d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
+ if(d != SQLITE_OK) {
+ infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+ throw FileNotGoodException("Cannot prepare read statement");
+ }
+
+ d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
+ if(d != SQLITE_OK) {
+ infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+ throw FileNotGoodException("Cannot prepare write statement");
+ }
+
+ d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
+ if(d != SQLITE_OK) {
+ infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+ throw FileNotGoodException("Cannot prepare read statement");
+ }
+
+ infostream<<"ServerMap: Database opened"<<std::endl;
+ }
+}
+
+bool ServerMap::loadFromFolders() {
+ if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
+ return true;
+ return false;
+}
+
+sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
+ return (sqlite3_int64)pos.Z*16777216 +
+ (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
+}
+
+void ServerMap::createDirs(std::string path)
+{
+ if(fs::CreateAllDirs(path) == false)
+ {
+ m_dout<<DTIME<<"ServerMap: Failed to create directory "
+ <<"\""<<path<<"\""<<std::endl;
+ throw BaseException("ServerMap failed to create directory");
+ }
+}
+
+std::string ServerMap::getSectorDir(v2s16 pos, int layout)
+{
+ char cc[9];
+ switch(layout)
+ {
+ case 1:
+ snprintf(cc, 9, "%.4x%.4x",
+ (unsigned int)pos.X&0xffff,
+ (unsigned int)pos.Y&0xffff);
+
+ return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
+ case 2:
+ snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
+ (unsigned int)pos.X&0xfff,
+ (unsigned int)pos.Y&0xfff);
+
+ return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
+ default:
assert(false);
}
}
{
unsigned int x, y;
int r;
- size_t spos = dirname.rfind('/') + 1;
+ size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
assert(spos != std::string::npos);
if(dirname.size() - spos == 8)
{
else if(dirname.size() - spos == 3)
{
// New layout
- r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
+ r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
// Sign-extend the 12 bit values up to 16 bits...
if(x&0x800) x|=0xF000;
if(y&0x800) y|=0xF000;
return cc;
}
-void ServerMap::save(bool only_changed)
+void ServerMap::save(ModifiedState save_level)
{
DSTACK(__FUNCTION_NAME);
if(m_map_saving_enabled == false)
{
- dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
+ infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
return;
}
- if(only_changed == false)
- dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
+ 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();
}
+ // Profile modified reasons
+ Profiler modprofiler;
+
u32 sector_meta_count = 0;
u32 block_count = 0;
u32 block_count_all = 0; // Number of blocks in memory
+ // Don't do anything with sqlite unless something is really saved
+ bool save_started = false;
+
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
for(; i.atEnd() == false; i++)
{
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++;
core::list<MapBlock*> blocks;
sector->getBlocks(blocks);
core::list<MapBlock*>::Iterator j;
+
for(j=blocks.begin(); j!=blocks.end(); j++)
{
MapBlock *block = *j;
block_count_all++;
- if(block->getModified() >= MOD_STATE_WRITE_NEEDED
- || only_changed == false)
+ if(block->getModified() >= save_level)
{
+ // Lazy beginSave()
+ if(!save_started){
+ beginSave();
+ save_started = true;
+ }
+
+ modprofiler.add(block->getModifiedReason(), 1);
+
saveBlock(block);
block_count++;
- /*dstream<<"ServerMap: Written block ("
+ /*infostream<<"ServerMap: Written block ("
<<block->getPos().X<<","
<<block->getPos().Y<<","
<<block->getPos().Z<<")"
}
}
}
+ if(save_started)
+ endSave();
/*
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)
{
- dstream<<DTIME<<"ServerMap: Written: "
+ infostream<<"ServerMap: Written: "
<<sector_meta_count<<" sector metadata files, "
<<block_count<<" block files"
<<", "<<block_count_all<<" blocks in memory."
<<std::endl;
+ PrintInfo(infostream); // ServerMap/ClientMap:
+ infostream<<"Blocks modified by: "<<std::endl;
+ modprofiler.print(infostream);
+ }
+}
+
+static s32 unsignedToSigned(s32 i, s32 max_positive)
+{
+ if(i < max_positive)
+ return i;
+ else
+ return i - 2*max_positive;
+}
+
+// modulo of a negative number does not work consistently in C
+static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
+{
+ if(i >= 0)
+ return i % mod;
+ return mod - ((-i) % mod);
+}
+
+v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
+{
+ s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
+ i = (i - x) / 4096;
+ s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
+ i = (i - y) / 4096;
+ s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
+ return v3s16(x,y,z);
+}
+
+void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
+{
+ if(loadFromFolders()){
+ errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
+ <<"all blocks that are stored in flat files"<<std::endl;
+ }
+
+ {
+ verifyDatabase();
+
+ while(sqlite3_step(m_database_list) == SQLITE_ROW)
+ {
+ sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
+ v3s16 p = getIntegerAsBlock(block_i);
+ //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
+ dst.push_back(p);
+ }
}
}
{
DSTACK(__FUNCTION_NAME);
- dstream<<"INFO: ServerMap::saveMapMeta(): "
+ /*infostream<<"ServerMap::saveMapMeta(): "
<<"seed="<<m_seed
- <<std::endl;
+ <<std::endl;*/
createDirs(m_savedir);
- std::string fullpath = m_savedir + "/map_meta.txt";
+ std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
std::ofstream os(fullpath.c_str(), std::ios_base::binary);
if(os.good() == false)
{
- dstream<<"ERROR: ServerMap::saveMapMeta(): "
+ infostream<<"ERROR: ServerMap::saveMapMeta(): "
<<"could not open"<<fullpath<<std::endl;
throw FileNotGoodException("Cannot open chunk metadata");
}
{
DSTACK(__FUNCTION_NAME);
- dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
- <<std::endl;
+ /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
+ <<std::endl;*/
- std::string fullpath = m_savedir + "/map_meta.txt";
+ std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
if(is.good() == false)
{
- dstream<<"ERROR: ServerMap::loadMapMeta(): "
+ infostream<<"ERROR: ServerMap::loadMapMeta(): "
<<"could not open"<<fullpath<<std::endl;
throw FileNotGoodException("Cannot open map metadata");
}
m_seed = params.getU64("seed");
- dstream<<"INFO: ServerMap::loadMapMeta(): "
- <<"seed="<<m_seed
- <<std::endl;
+ verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
}
void ServerMap::saveSectorMeta(ServerMapSector *sector)
std::string dir = getSectorDir(pos);
createDirs(dir);
- std::string fullpath = dir + "/meta";
+ std::string fullpath = dir + DIR_DELIM + "meta";
std::ofstream o(fullpath.c_str(), std::ios_base::binary);
if(o.good() == false)
throw FileNotGoodException("Cannot open sector metafile");
ServerMapSector *sector = NULL;
- std::string fullpath = sectordir + "/meta";
+ std::string fullpath = sectordir + DIR_DELIM + "meta";
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
if(is.good() == false)
{
// format. Just go ahead and create the sector.
if(fs::PathExists(sectordir))
{
- /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
+ /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
<<fullpath<<" doesn't exist but directory does."
<<" Continuing with a sector with no metadata."
<<std::endl;*/
- sector = new ServerMapSector(this, p2d);
+ sector = new ServerMapSector(this, p2d, m_gamedef);
m_sectors.insert(p2d, sector);
}
else
else
{
sector = ServerMapSector::deSerialize
- (is, this, p2d, m_sectors);
+ (is, this, p2d, m_sectors, m_gamedef);
if(save_after_load)
saveSectorMeta(sector);
}
if(loadlayout != 2)
{
- dstream<<"Sector converted to new layout - deleting "<<
+ infostream<<"Sector converted to new layout - deleting "<<
sectordir1<<std::endl;
fs::RecursiveDelete(sectordir1);
}
}
#endif
+void ServerMap::beginSave() {
+ verifyDatabase();
+ if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
+ infostream<<"WARNING: beginSave() failed, saving might be slow.";
+}
+
+void ServerMap::endSave() {
+ verifyDatabase();
+ if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
+ infostream<<"WARNING: endSave() failed, map might not have saved.";
+}
+
void ServerMap::saveBlock(MapBlock *block)
{
DSTACK(__FUNCTION_NAME);
if(block->isDummy())
{
/*v3s16 p = block->getPos();
- dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
+ infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
return;
}
// Get destination
v3s16 p3d = block->getPos();
+
+#if 0
v2s16 p2d(p3d.X, p3d.Z);
std::string sectordir = getSectorDir(p2d);
createDirs(sectordir);
- std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
+ std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
std::ofstream o(fullpath.c_str(), std::ios_base::binary);
if(o.good() == false)
throw FileNotGoodException("Cannot open block data");
-
+#endif
/*
[0] u8 serialization version
[1] data
*/
+
+ verifyDatabase();
+
+ std::ostringstream o(std::ios_base::binary);
+
o.write((char*)&version, 1);
// Write basic data
- block->serialize(o, version);
+ block->serialize(o, version, true);
+
+ // Write block to database
+
+ std::string tmp = o.str();
+ const char *bytes = tmp.c_str();
+
+ if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
+ infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
+ if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
+ infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
+ int written = sqlite3_step(m_database_write);
+ if(written != SQLITE_DONE)
+ infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
+ <<sqlite3_errmsg(m_database)<<std::endl;
+ // Make ready for later reuse
+ sqlite3_reset(m_database_write);
- // Write extra data stored on disk
- block->serializeDiskExtra(o, version);
-
// We just wrote it to the disk so clear modified flag
block->resetModified();
}
{
DSTACK(__FUNCTION_NAME);
- std::string fullpath = sectordir+"/"+blockfile;
+ std::string fullpath = sectordir+DIR_DELIM+blockfile;
try{
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
}
// 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);
if(version < SER_FMT_VER_HIGHEST || save_after_load)
{
saveBlock(block);
+
+ // Should be in database now, so delete the old file
+ fs::RecursiveDelete(fullpath);
}
// We just loaded it from the disk, so it's up-to-date.
}
catch(SerializationError &e)
{
- dstream<<"WARNING: Invalid block data on disk "
+ infostream<<"WARNING: Invalid block data on disk "
<<"fullpath="<<fullpath
<<" (SerializationError). "
<<"what()="<<e.what()
}
}
+void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ try {
+ std::istringstream is(*blob, std::ios_base::binary);
+
+ u8 version = SER_FMT_VER_INVALID;
+ is.read((char*)&version, 1);
+
+ if(is.fail())
+ throw SerializationError("ServerMap::loadBlock(): Failed"
+ " to read MapBlock version");
+
+ /*u32 block_size = MapBlock::serializedLength(version);
+ SharedBuffer<u8> data(block_size);
+ is.read((char*)*data, block_size);*/
+
+ // This will always return a sector because we're the server
+ //MapSector *sector = emergeSector(p2d);
+
+ MapBlock *block = NULL;
+ bool created_new = false;
+ block = sector->getBlockNoCreateNoEx(p3d.Y);
+ if(block == NULL)
+ {
+ block = sector->createBlankBlockNoInsert(p3d.Y);
+ created_new = true;
+ }
+
+ // Read basic data
+ block->deSerialize(is, version, true);
+
+ // If it's a new block, insert it to the map
+ if(created_new)
+ sector->insertBlock(block);
+
+ /*
+ Save blocks loaded in old format in new format
+ */
+
+ //if(version < SER_FMT_VER_HIGHEST || save_after_load)
+ // Only save if asked to; no need to update version
+ if(save_after_load)
+ saveBlock(block);
+
+ // We just loaded it from, so it's up-to-date.
+ block->resetModified();
+
+ }
+ 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);
+
+ // TODO: Copy to a backup database.
+ }
+}
+
MapBlock* ServerMap::loadBlock(v3s16 blockpos)
{
DSTACK(__FUNCTION_NAME);
v2s16 p2d(blockpos.X, blockpos.Z);
+ if(!loadFromFolders()) {
+ verifyDatabase();
+
+ if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
+ infostream<<"WARNING: Could not bind block position for load: "
+ <<sqlite3_errmsg(m_database)<<std::endl;
+ if(sqlite3_step(m_database_read) == SQLITE_ROW) {
+ /*
+ Make sure sector is loaded
+ */
+ MapSector *sector = createSector(p2d);
+
+ /*
+ Load block
+ */
+ const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
+ size_t len = sqlite3_column_bytes(m_database_read, 0);
+
+ std::string datastr(data, len);
+
+ loadBlock(&datastr, blockpos, sector, false);
+
+ sqlite3_step(m_database_read);
+ // We should never get more than 1 row, so ok to reset
+ sqlite3_reset(m_database_read);
+
+ return getBlockNoCreateNoEx(blockpos);
+ }
+ sqlite3_reset(m_database_read);
+
+ // Not found in database, try the files
+ }
+
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
*/
std::string blockfilename = getBlockFilename(blockpos);
- if(fs::PathExists(sectordir+"/"+blockfilename) == false)
+ if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
return NULL;
/*
- Load block
+ Load block and save it to the database
*/
- loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
+ loadBlock(sectordir, blockfilename, sector, true);
return getBlockNoCreateNoEx(blockpos);
}
out<<"ServerMap: ";
}
-#ifndef SERVER
-
-/*
- ClientMap
-*/
-
-ClientMap::ClientMap(
- Client *client,
- MapDrawControl &control,
- scene::ISceneNode* parent,
- scene::ISceneManager* mgr,
- s32 id
-):
- Map(dout_client),
- 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_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);
-
- {
- //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();
-}
-
-void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
-{
- //m_dout<<DTIME<<"Rendering map..."<<std::endl;
- DSTACK(__FUNCTION_NAME);
-
- bool is_transparent_pass = pass == scene::ESNRP_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;
- m_camera_mutex.Unlock();
-
- /*
- Get all blocks and draw all visible ones
- */
-
- v3s16 cam_pos_nodes(
- camera_position.X / BS,
- camera_position.Y / BS,
- camera_position.Z / 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
- v3s16 p_blocks_min(
- p_nodes_min.X / MAP_BLOCKSIZE - 2,
- p_nodes_min.Y / MAP_BLOCKSIZE - 2,
- p_nodes_min.Z / MAP_BLOCKSIZE - 2);
- 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;
-
- // For limiting number of mesh updates per frame
- u32 mesh_update_count = 0;
-
- u32 blocks_would_have_drawn = 0;
- u32 blocks_drawn = 0;
-
- int timecheck_counter = 0;
- core::map<v2s16, MapSector*>::Iterator si;
- si = m_sectors.getIterator();
- for(; si.atEnd() == false; si++)
- {
- {
- timecheck_counter++;
- if(timecheck_counter > 50)
- {
- timecheck_counter = 0;
- int time2 = time(0);
- if(time2 > time1 + 4)
- {
- dstream<<"ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- <<std::endl;
- return;
- }
- }
- }
-
- 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);
-
- /*
- Draw blocks
- */
-
- 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, range, &d) == false)
- {
- continue;
- }
-
- // Okay, this block will be drawn. Reset usage timer.
- block->resetUsageTimer();
-
- // This is ugly (spherical distance limit?)
- /*if(m_control.range_all == false &&
- d - 0.5*BS*MAP_BLOCKSIZE > range)
- continue;*/
-
-#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)
- 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
- /*
- Draw the faces of the block
- */
- {
- JMutexAutoLock lock(block->mesh_mutex);
-
- scene::SMesh *mesh = block->mesh;
-
- if(mesh == NULL)
- continue;
-
- 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;
-
- blocks_drawn++;
- sector_blocks_drawn++;
-
- u32 c = mesh->getMeshBufferCount();
-
- 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)
- {
- /*
- This *shouldn't* hurt too much because Irrlicht
- doesn't change opengl textures if the old
- material is set again.
- */
- driver->setMaterial(buf->getMaterial());
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- }
- }
- }
- } // foreach sectorblocks
-
- if(sector_blocks_drawn != 0)
- {
- m_last_drawn_sectors[sp] = true;
- }
- }
-
- m_control.blocks_drawn = blocks_drawn;
- m_control.blocks_would_have_drawn = blocks_would_have_drawn;
-
- /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
- <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
-}
-
-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
*/
MapVoxelManipulator::~MapVoxelManipulator()
{
- /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
+ /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
<<std::endl;*/
}
{
TimeTaker timer1("emerge load", &emerge_load_time);
- /*dstream<<"Loading block (caller_id="<<caller_id<<")"
+ /*infostream<<"Loading block (caller_id="<<caller_id<<")"
<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<" wanted area: ";
- a.print(dstream);
- dstream<<std::endl;*/
+ a.print(infostream);
+ infostream<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreate(p);
if(block->isDummy())
m_loaded_blocks.insert(p, !block_data_inexistent);
}
- //dstream<<"emerge done"<<std::endl;
+ //infostream<<"emerge done"<<std::endl;
}
/*
//TimeTaker timer1("blitBack");
- /*dstream<<"blitBack(): m_loaded_blocks.size()="
+ /*infostream<<"blitBack(): m_loaded_blocks.size()="
<<m_loaded_blocks.size()<<std::endl;*/
/*
u32 size_MB = block_area_nodes.getVolume()*4/1000000;
if(size_MB >= 1)
{
- dstream<<"initialEmerge: area: ";
- block_area_nodes.print(dstream);
- dstream<<" ("<<size_MB<<"MB)";
- dstream<<std::endl;
+ infostream<<"initialEmerge: area: ";
+ block_area_nodes.print(infostream);
+ infostream<<" ("<<size_MB<<"MB)";
+ infostream<<std::endl;
}
addArea(block_area_nodes);
if(existed == false)
{
// The Great Bug was found using this
- /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
+ /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
<<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
continue;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block == NULL)
{
- dstream<<"WARNING: "<<__FUNCTION_NAME
+ infostream<<"WARNING: "<<__FUNCTION_NAME
<<": got NULL block "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;