X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.cpp;h=3b02ac02fc2dfd021a65cde98bd567428cffb160;hb=d74385be24f0c11b34d4af6a92c7bc8d18e5748e;hp=e8115d39b8481e4c548f0709c65df975b80cee03;hpb=f7ee5da4c115231df3f4897db04fd308749efe55;p=dragonfireclient.git diff --git a/src/map.cpp b/src/map.cpp index e8115d39b..3b02ac02f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -20,9 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "mapsector.h" #include "mapblock.h" -#include "main.h" #include "filesys.h" #include "voxel.h" +#include "voxelalgorithms.h" #include "porting.h" #include "serialization.h" #include "nodemetadata.h" @@ -32,39 +32,31 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "gamedef.h" #include "util/directiontables.h" -#include "util/mathconstants.h" +#include "util/basic_macros.h" #include "rollback_interface.h" #include "environment.h" +#include "reflowscan.h" #include "emerge.h" #include "mapgen_v6.h" -#include "biome.h" +#include "mg_biome.h" #include "config.h" #include "server.h" #include "database.h" #include "database-dummy.h" #include "database-sqlite3.h" +#include "script/scripting_server.h" +#include +#include #if USE_LEVELDB #include "database-leveldb.h" #endif +#if USE_REDIS +#include "database-redis.h" +#endif +#if USE_POSTGRESQL +#include "database-postgresql.h" +#endif -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - -/* - 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 @@ -73,10 +65,13 @@ with this program; if not, write to the Free Software Foundation, Inc., Map::Map(std::ostream &dout, IGameDef *gamedef): m_dout(dout), m_gamedef(gamedef), - m_sector_cache(NULL) + m_sector_cache(NULL), + m_nodedef(gamedef->ndef()), + m_transforming_liquid_loop_count_multiplier(1.0f), + m_unprocessed_count(0), + m_inc_trending_up_start_time(0), + m_queue_size_timer_started(false) { - /*m_sector_mutex.Init(); - assert(m_sector_mutex.IsInitialized());*/ } Map::~Map() @@ -180,31 +175,47 @@ bool Map::isNodeUnderground(v3s16 p) bool Map::isValidPosition(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); - MapBlock *block = getBlockNoCreate(blockpos); + MapBlock *block = getBlockNoCreateNoEx(blockpos); return (block != NULL); } // Returns a CONTENT_IGNORE node if not found -MapNode Map::getNodeNoEx(v3s16 p) +MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position) { v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block == NULL) + if (block == NULL) { + if (is_valid_position != NULL) + *is_valid_position = false; return MapNode(CONTENT_IGNORE); + } + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - return block->getNodeNoCheck(relpos); + bool is_valid_p; + MapNode node = block->getNodeNoCheck(relpos, &is_valid_p); + if (is_valid_position != NULL) + *is_valid_position = is_valid_p; + return node; } +#if 0 +// Deprecated // throws InvalidPositionException if not found +// TODO: Now this is deprecated, getNodeNoEx should be renamed MapNode Map::getNode(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block == NULL) + if (block == NULL) throw InvalidPositionException(); v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - return block->getNodeNoCheck(relpos); + bool is_valid_position; + MapNode node = block->getNodeNoCheck(relpos, &is_valid_position); + if (!is_valid_position) + throw InvalidPositionException(); + return node; } +#endif // throws InvalidPositionException if not found void Map::setNode(v3s16 p, MapNode & n) @@ -214,1060 +225,43 @@ void Map::setNode(v3s16 p, MapNode & n) v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; // Never allow placing CONTENT_IGNORE, it fucks up stuff if(n.getContent() == CONTENT_IGNORE){ + bool temp_bool; errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE" <<" while trying to replace \"" - <ndef()->get(block->getNodeNoCheck(relpos)).name + <get(block->getNodeNoCheck(relpos, &temp_bool)).name <<"\" at "<setNodeNoCheck(relpos, n); -} - - -/* - Goes recursively through the neighbours of the node. - - Alters only transparent nodes. - - If the lighting of the neighbour is lower than the lighting of - the node was (before changing it to 0 at the step before), the - lighting of the neighbour is set to 0 and then the same stuff - repeats for the neighbour. - - The ending nodes of the routine are stored in light_sources. - This is useful when a light is removed. In such case, this - routine can be called for the light node and then again for - light_sources to re-light the area without the removed light. - - values of from_nodes are lighting values. -*/ -void Map::unspreadLight(enum LightBank bank, - std::map & from_nodes, - std::set & light_sources, - std::map & modified_blocks) -{ - INodeDefManager *nodemgr = m_gamedef->ndef(); - - v3s16 dirs[6] = { - 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 - }; - - if(from_nodes.size() == 0) - return; - - u32 blockchangecount = 0; - - std::map unlighted_nodes; - - /* - Initialize block cache - */ - v3s16 blockpos_last; - MapBlock *block = NULL; - // Cache this a bit, too - bool block_checked_in_modified = false; - - for(std::map::iterator j = from_nodes.begin(); - j != from_nodes.end(); ++j) - { - v3s16 pos = j->first; - v3s16 blockpos = getNodeBlockPos(pos); - - // Only fetch a new block if the block position has changed - try{ - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) - { - continue; - } - - if(block->isDummy()) - continue; - - // Calculate relative position in block - //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; - - // Get node straight from the block - //MapNode n = block->getNode(relpos); - - u8 oldlight = j->second; - - // Loop through 6 neighbors - for(u16 i=0; i<6; i++) - { - // Get the position of the neighbor node - v3s16 n2pos = pos + dirs[i]; - - // Get the block where the node is located - v3s16 blockpos = getNodeBlockPos(n2pos); - - try - { - // Only fetch a new block if the block position has changed - try{ - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) - { - continue; - } - - // Calculate relative position in block - v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE; - // Get node straight from the block - MapNode n2 = block->getNode(relpos); - - bool changed = false; - - //TODO: Optimize output by optimizing light_sources? - - /* - If the neighbor is dimmer than what was specified - as oldlight (the light of the previous node) - */ - if(n2.getLight(bank, nodemgr) < oldlight) - { - /* - And the neighbor is transparent and it has some light - */ - 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, nodemgr); - n2.setLight(bank, 0, nodemgr); - block->setNode(relpos, n2); - - unlighted_nodes[n2pos] = current_light; - changed = true; - - /* - Remove from light_sources if it is there - NOTE: This doesn't happen nearly at all - */ - /*if(light_sources.find(n2pos)) - { - infostream<<"Removed from light_sources"< 0) - unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks); -} - -/* - A single-node wrapper of the above -*/ -void Map::unLightNeighbors(enum LightBank bank, - v3s16 pos, u8 lightwas, - std::set & light_sources, - std::map & modified_blocks) -{ - std::map from_nodes; - from_nodes[pos] = lightwas; - - unspreadLight(bank, from_nodes, light_sources, modified_blocks); -} - -/* - Lights neighbors of from_nodes, collects all them and then - goes on recursively. -*/ -void Map::spreadLight(enum LightBank bank, - std::set & from_nodes, - std::map & modified_blocks) -{ - INodeDefManager *nodemgr = m_gamedef->ndef(); - - const v3s16 dirs[6] = { - 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 - }; - - if(from_nodes.size() == 0) - return; - - u32 blockchangecount = 0; - - std::set lighted_nodes; - - /* - Initialize block cache - */ - v3s16 blockpos_last; - MapBlock *block = NULL; - // Cache this a bit, too - bool block_checked_in_modified = false; - - for(std::set::iterator j = from_nodes.begin(); - j != from_nodes.end(); ++j) - { - v3s16 pos = *j; - v3s16 blockpos = getNodeBlockPos(pos); - - // Only fetch a new block if the block position has changed - try{ - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) - { - continue; - } - - if(block->isDummy()) - continue; - - // Calculate relative position in block - v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; - - // Get node straight from the block - MapNode n = block->getNode(relpos); - - u8 oldlight = n.getLight(bank, nodemgr); - u8 newlight = diminish_light(oldlight); - - // Loop through 6 neighbors - for(u16 i=0; i<6; i++){ - // Get the position of the neighbor node - v3s16 n2pos = pos + dirs[i]; - - // Get the block where the node is located - v3s16 blockpos = getNodeBlockPos(n2pos); - - try - { - // Only fetch a new block if the block position has changed - try{ - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) - { - continue; - } - - // Calculate relative position in block - v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE; - // Get node straight from the block - MapNode n2 = block->getNode(relpos); - - bool changed = false; - /* - 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, nodemgr) > undiminish_light(oldlight)) - { - lighted_nodes.insert(n2pos); - changed = true; - } - /* - If the neighbor is dimmer than how much light this node - would spread on it, add to list - */ - if(n2.getLight(bank, nodemgr) < newlight) - { - if(nodemgr->get(n2).light_propagates) - { - n2.setLight(bank, newlight, nodemgr); - block->setNode(relpos, n2); - lighted_nodes.insert(n2pos); - changed = true; - } - } - - // Add to modified_blocks - if(changed == true && block_checked_in_modified == false) - { - // If the block is not found in modified_blocks, add. - if(modified_blocks.find(blockpos) == modified_blocks.end()) - { - modified_blocks[blockpos] = block; - } - block_checked_in_modified = true; - } - } - catch(InvalidPositionException &e) - { - continue; - } - } - } - - /*infostream<<"spreadLight(): Changed block " - < 0) - spreadLight(bank, lighted_nodes, modified_blocks); -} - -/* - A single-node source variation of the above. -*/ -void Map::lightNeighbors(enum LightBank bank, - v3s16 pos, - std::map & modified_blocks) -{ - std::set from_nodes; - from_nodes.insert(pos); - spreadLight(bank, from_nodes, modified_blocks); -} - -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 - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - - u8 brightest_light = 0; - v3s16 brightest_pos(0,0,0); - bool found_something = false; - - // Loop through 6 neighbors - for(u16 i=0; i<6; i++){ - // Get the position of the neighbor node - v3s16 n2pos = p + dirs[i]; - MapNode n2; - try{ - n2 = getNode(n2pos); - } - catch(InvalidPositionException &e) - { - continue; - } - if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){ - brightest_light = n2.getLight(bank, nodemgr); - brightest_pos = n2pos; - found_something = true; - } - } - - if(found_something == false) - throw InvalidPositionException(); - - return brightest_pos; -} - -/* - Propagates sunlight down from a node. - Starting point gets sunlight. - - Returns the lowest y value of where the sunlight went. - - Mud is turned into grass in where the sunlight stops. -*/ -s16 Map::propagateSunlight(v3s16 start, - std::map & modified_blocks) -{ - INodeDefManager *nodemgr = m_gamedef->ndef(); - - s16 y = start.Y; - for(; ; y--) - { - v3s16 pos(start.X, y, start.Z); - - v3s16 blockpos = getNodeBlockPos(pos); - MapBlock *block; - try{ - block = getBlockNoCreate(blockpos); - } - catch(InvalidPositionException &e) - { - break; - } - - v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE; - MapNode n = block->getNode(relpos); - - if(nodemgr->get(n).sunlight_propagates) - { - n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr); - block->setNode(relpos, n); - - modified_blocks[blockpos] = block; - } - else - { - // Sunlight goes no further - break; - } - } - return y + 1; -} - -void Map::updateLighting(enum LightBank bank, - std::map & a_blocks, - std::map & modified_blocks) -{ - INodeDefManager *nodemgr = m_gamedef->ndef(); - - /*m_dout< blocks_to_update; - - std::set light_sources; - - std::map unlight_from; - - int num_bottom_invalid = 0; - - { - //TimeTaker t("first stuff"); - - for(std::map::iterator i = a_blocks.begin(); - i != a_blocks.end(); ++i) - { - MapBlock *block = i->second; - - for(;;) - { - // Don't bother with dummy blocks. - if(block->isDummy()) - break; - - v3s16 pos = block->getPos(); - v3s16 posnodes = block->getPosRelative(); - modified_blocks[pos] = block; - blocks_to_update[pos] = block; - - /* - Clear all light from block - */ - for(s16 z=0; zgetNode(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.insert(p + posnodes); - - // Collect borders for unlighting - 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 + posnodes; - unlight_from[p_map] = oldlight; - } - } - catch(InvalidPositionException &e) - { - /* - This would happen when dealing with a - dummy block. - */ - //assert(0); - infostream<<"updateLighting(): InvalidPositionException" - <propagateSunlight(light_sources); - - if(!bottom_valid) - num_bottom_invalid++; - - // If bottom is valid, we're done. - if(bottom_valid) - break; - } - else if(bank == LIGHTBANK_NIGHT) - { - // For night lighting, sunlight is not propagated - break; - } - else - { - // Invalid lighting bank - assert(0); - } - - /*infostream<<"Bottom for sunlight-propagated block (" - <get("")) - { - core::map::Iterator i; - i = blocks_to_update.getIterator(); - for(; i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - block->setLightingExpired(false); - } - return; - } -#endif - -#if 1 - { - //TimeTaker timer("unspreadLight"); - unspreadLight(bank, unlight_from, light_sources, modified_blocks); - } - - /*if(debug) - { - u32 diff = modified_blocks.size() - count_was; - count_was = modified_blocks.size(); - infostream<<"unspreadLight modified "<::Iterator i; - i = blocks_to_update.getIterator(); - for(; i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - - // Add all surrounding blocks - vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1)); - - /* - Add all surrounding blocks that have up-to-date lighting - NOTE: This doesn't quite do the job (not everything - appropriate is lighted) - */ - /*for(s16 z=-1; z<=1; z++) - for(s16 y=-1; y<=1; y++) - for(s16 x=-1; x<=1; x++) - { - 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(p2, p2); - }*/ - - // Lighting of block will be updated completely - block->setLightingExpired(false); - } - } - - { - //TimeTaker timer("unSpreadLight"); - vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr); - } - { - //TimeTaker timer("spreadLight"); - vmanip.spreadLight(bank, light_sources, nodemgr); - } - { - //TimeTaker timer("blitBack"); - vmanip.blitBack(modified_blocks); - } - /*infostream<<"emerge_time="< & a_blocks, - std::map & modified_blocks) -{ - updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks); - updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks); - - /* - Update information about whether day and night light differ - */ - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - MapBlock *block = i->second; - block->expireDayNightDiff(); - } -} - -/* -*/ -void Map::addNodeAndUpdate(v3s16 p, MapNode n, - std::map &modified_blocks) -{ - INodeDefManager *ndef = m_gamedef->ndef(); - - /*PrintInfo(m_dout); - m_dout< 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, - there has not been any sunlight going down. - - Otherwise there probably is. - */ - try{ - MapNode topnode = getNode(toppos); - - if(topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) - node_under_sunlight = false; - } - catch(InvalidPositionException &e) - { - } - - /* - Remove all light that has come out of this node - */ - - enum LightBank banks[] = - { - LIGHTBANK_DAY, - LIGHTBANK_NIGHT - }; - for(s32 i=0; i<2; i++) - { - enum LightBank bank = banks[i]; - - u8 lightwas = getNode(p).getLight(bank, ndef); - - // Add the block of the added node to modified_blocks - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * block = getBlockNoCreate(blockpos); - assert(block != NULL); - modified_blocks[blockpos] = block; - - assert(isValidPosition(p)); - - // Unlight neighbours of node. - // This means setting light of all consequent dimmer nodes - // to 0. - // This also collects the nodes at the border which will spread - // light again into this. - unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks); - - n.setLight(bank, 0, ndef); - } - - /* - If node lets sunlight through and is under sunlight, it has - sunlight too. - */ - if(node_under_sunlight && ndef->get(n).sunlight_propagates) - { - n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef); - } - - /* - Remove node metadata - */ - - removeNodeMetadata(p); - - /* - Set the node on the map - */ - - setNode(p, n); - - /* - If node is under sunlight and doesn't let sunlight through, - take all sunlighted nodes under it and clear light from them - and from where the light has been spread. - TODO: This could be optimized by mass-unlighting instead - of looping - */ - if(node_under_sunlight && !ndef->get(n).sunlight_propagates) - { - s16 y = p.Y - 1; - for(;; y--){ - //m_dout<::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - i->second->expireDayNightDiff(); - } - - /* - Report for rollback - */ - if(m_gamedef->rollback()) - { - RollbackNode rollback_newnode(this, p, m_gamedef); - RollbackAction action; - action.setSetNode(p, rollback_oldnode, rollback_newnode); - m_gamedef->rollback()->reportAction(action); - } - - /* - Add neighboring liquid nodes and the node itself if it is - liquid (=water node was added) to transform queue. - note: todo: for liquid_finite enough to add only self node - */ - v3s16 dirs[7] = { - v3s16(0,0,0), // self - 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++) - { - try - { - - v3s16 p2 = p + dirs[i]; - - MapNode n2 = getNode(p2); - if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR) - { - m_transforming_liquid.push_back(p2); - } - - }catch(InvalidPositionException &e) - { - } - } -} - -/* -*/ -void Map::removeNodeAndUpdate(v3s16 p, - std::map &modified_blocks) -{ - INodeDefManager *ndef = m_gamedef->ndef(); - - /*PrintInfo(m_dout); - m_dout<setNodeNoCheck(relpos, n); +} - std::set light_sources; +void Map::addNodeAndUpdate(v3s16 p, MapNode n, + std::map &modified_blocks, + bool remove_metadata) +{ + // Collect old node for rollback + RollbackNode rollback_oldnode(this, p, m_gamedef); - enum LightBank banks[] = - { - LIGHTBANK_DAY, - LIGHTBANK_NIGHT - }; - for(s32 i=0; i<2; i++) - { - enum LightBank bank = banks[i]; + // This is needed for updating the lighting + MapNode oldnode = getNodeNoEx(p); - /* - Unlight neighbors (in case the node is a light source) - */ - unLightNeighbors(bank, p, - getNode(p).getLight(bank, ndef), - light_sources, modified_blocks); + // Remove node metadata + if (remove_metadata) { + removeNodeMetadata(p); } - /* - Remove node metadata - */ - - removeNodeMetadata(p); - - /* - Remove the node. - This also clears the lighting. - */ - - MapNode n; - n.setContent(replace_material); + // Set the node on the map + // Ignore light (because calling voxalgo::update_lighting_nodes) + n.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); setNode(p, n); - for(s32 i=0; i<2; i++) - { - enum LightBank bank = banks[i]; - - /* - Recalculate lighting - */ - spreadLight(bank, light_sources, modified_blocks); - } - - // Add the block of the removed node to modified_blocks - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * block = getBlockNoCreate(blockpos); - assert(block != NULL); - modified_blocks[blockpos] = block; - - /* - If the removed node was under sunlight, propagate the - sunlight down from it and then light all neighbors - of the propagated blocks. - */ - if(node_under_sunlight) - { - s16 ybottom = propagateSunlight(p, modified_blocks); - /*m_dout< ybottom="<= ybottom; y--) - { - v3s16 p2(p.X, y, p.Z); - /*m_dout< > oldnodes; + oldnodes.push_back(std::pair(p, oldnode)); + voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks); - /* - Update information about whether day and night light differ - */ for(std::map::iterator i = modified_blocks.begin(); i != modified_blocks.end(); ++i) @@ -1275,9 +269,7 @@ void Map::removeNodeAndUpdate(v3s16 p, i->second->expireDayNightDiff(); } - /* - Report for rollback - */ + // Report for rollback if(m_gamedef->rollback()) { RollbackNode rollback_newnode(this, p, m_gamedef); @@ -1288,9 +280,8 @@ void Map::removeNodeAndUpdate(v3s16 p, /* Add neighboring liquid nodes and this node to transform queue. - (it's vital for the node itself to get updated last.) - note: todo: for liquid_finite enough to add only self node - */ + (it's vital for the node itself to get updated last, if it was removed.) + */ v3s16 dirs[7] = { v3s16(0,0,1), // back v3s16(0,1,0), // top @@ -1302,34 +293,34 @@ void Map::removeNodeAndUpdate(v3s16 p, }; for(u16 i=0; i<7; i++) { - try - { - v3s16 p2 = p + dirs[i]; - MapNode n2 = getNode(p2); - if(ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR) - { + bool is_valid_position; + MapNode n2 = getNodeNoEx(p2, &is_valid_position); + if(is_valid_position && + (m_nodedef->get(n2).isLiquid() || + n2.getContent() == CONTENT_AIR)) m_transforming_liquid.push_back(p2); - } - - }catch(InvalidPositionException &e) - { - } } } -bool Map::addNodeWithEvent(v3s16 p, MapNode n) +void Map::removeNodeAndUpdate(v3s16 p, + std::map &modified_blocks) +{ + addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true); +} + +bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata) { MapEditEvent event; - event.type = MEET_ADDNODE; + event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE; event.p = p; event.n = n; bool succeeded = true; try{ std::map modified_blocks; - addNodeAndUpdate(p, n, modified_blocks); + addNodeAndUpdate(p, n, modified_blocks, remove_metadata); // Copy modified_blocks to event for(std::map::iterator @@ -1433,71 +424,140 @@ bool Map::getDayNightDiff(v3s16 blockpos) return false; } +struct TimeOrderedMapBlock { + MapSector *sect; + MapBlock *block; + + TimeOrderedMapBlock(MapSector *sect, MapBlock *block) : + sect(sect), + block(block) + {} + + bool operator<(const TimeOrderedMapBlock &b) const + { + return block->getUsageTimer() < b.block->getUsageTimer(); + }; +}; + /* Updates usage timers */ -void Map::timerUpdate(float dtime, float unload_timeout, - std::list *unloaded_blocks) +void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, + std::vector *unloaded_blocks) { bool save_before_unloading = (mapType() == MAPTYPE_SERVER); // Profile modified reasons Profiler modprofiler; - std::list sector_deletion_queue; + std::vector sector_deletion_queue; u32 deleted_blocks_count = 0; u32 saved_blocks_count = 0; u32 block_count_all = 0; beginSave(); - for(std::map::iterator si = m_sectors.begin(); - si != m_sectors.end(); ++si) - { - MapSector *sector = si->second; - bool all_blocks_deleted = true; + // If there is no practical limit, we spare creation of mapblock_queue + if (max_loaded_blocks == U32_MAX) { + for (std::map::iterator si = m_sectors.begin(); + si != m_sectors.end(); ++si) { + MapSector *sector = si->second; - std::list blocks; - sector->getBlocks(blocks); + bool all_blocks_deleted = true; - for(std::list::iterator i = blocks.begin(); - i != blocks.end(); ++i) - { - MapBlock *block = (*i); + MapBlockVect blocks; + sector->getBlocks(blocks); - block->incrementUsageTimer(dtime); + for (MapBlockVect::iterator i = blocks.begin(); + i != blocks.end(); ++i) { + MapBlock *block = (*i); - if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) - { - v3s16 p = block->getPos(); + block->incrementUsageTimer(dtime); - // Save if modified - if(block->getModified() != MOD_STATE_CLEAN - && save_before_unloading) - { - modprofiler.add(block->getModifiedReason(), 1); - saveBlock(block); - saved_blocks_count++; - } + if (block->refGet() == 0 + && block->getUsageTimer() > unload_timeout) { + v3s16 p = block->getPos(); + + // Save if modified + if (block->getModified() != MOD_STATE_CLEAN + && save_before_unloading) { + modprofiler.add(block->getModifiedReasonString(), 1); + if (!saveBlock(block)) + continue; + saved_blocks_count++; + } - // Delete from memory - sector->deleteBlock(block); + // Delete from memory + sector->deleteBlock(block); - if(unloaded_blocks) - unloaded_blocks->push_back(p); + if (unloaded_blocks) + unloaded_blocks->push_back(p); - deleted_blocks_count++; + deleted_blocks_count++; + } else { + all_blocks_deleted = false; + block_count_all++; + } } - else - { - all_blocks_deleted = false; - block_count_all++; + + if (all_blocks_deleted) { + sector_deletion_queue.push_back(si->first); } } + } else { + std::priority_queue mapblock_queue; + for (std::map::iterator si = m_sectors.begin(); + si != m_sectors.end(); ++si) { + MapSector *sector = si->second; - if(all_blocks_deleted) - { - sector_deletion_queue.push_back(si->first); + MapBlockVect blocks; + sector->getBlocks(blocks); + + for(MapBlockVect::iterator i = blocks.begin(); + i != blocks.end(); ++i) { + MapBlock *block = (*i); + + block->incrementUsageTimer(dtime); + mapblock_queue.push(TimeOrderedMapBlock(sector, block)); + } + } + block_count_all = mapblock_queue.size(); + // Delete old blocks, and blocks over the limit from the memory + while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks + || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) { + TimeOrderedMapBlock b = mapblock_queue.top(); + mapblock_queue.pop(); + + MapBlock *block = b.block; + + if (block->refGet() != 0) + continue; + + v3s16 p = block->getPos(); + + // Save if modified + if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) { + modprofiler.add(block->getModifiedReasonString(), 1); + if (!saveBlock(block)) + continue; + saved_blocks_count++; + } + + // Delete from memory + b.sect->deleteBlock(block); + + if (unloaded_blocks) + unloaded_blocks->push_back(p); + + deleted_blocks_count++; + block_count_all--; + } + // Delete empty sectors + for (std::map::iterator si = m_sectors.begin(); + si != m_sectors.end(); ++si) { + if (si->second->empty()) { + sector_deletion_queue.push_back(si->first); + } } } endSave(); @@ -1522,16 +582,15 @@ void Map::timerUpdate(float dtime, float unload_timeout, } } -void Map::unloadUnreferencedBlocks(std::list *unloaded_blocks) +void Map::unloadUnreferencedBlocks(std::vector *unloaded_blocks) { - timerUpdate(0.0, -1.0, unloaded_blocks); + timerUpdate(0.0, -1.0, 0, unloaded_blocks); } -void Map::deleteSectors(std::list &list) +void Map::deleteSectors(std::vector §orList) { - for(std::list::iterator j = list.begin(); - j != list.end(); ++j) - { + for(std::vector::iterator j = sectorList.begin(); + j != sectorList.end(); ++j) { MapSector *sector = m_sectors[*j]; // If sector is in sector cache, remove it from there if(m_sector_cache == sector) @@ -1542,63 +601,6 @@ void Map::deleteSectors(std::list &list) } } -#if 0 -void Map::unloadUnusedData(float timeout, - core::list *deleted_blocks) -{ - core::list sector_deletion_queue; - u32 deleted_blocks_count = 0; - u32 saved_blocks_count = 0; - - core::map::Iterator si = m_sectors.getIterator(); - for(; si.atEnd() == false; si++) - { - MapSector *sector = si.getNode()->getValue(); - - bool all_blocks_deleted = true; - - core::list blocks; - sector->getBlocks(blocks); - for(core::list::Iterator i = blocks.begin(); - i != blocks.end(); i++) - { - MapBlock *block = (*i); - - if(block->getUsageTimer() > timeout) - { - // Save if modified - if(block->getModified() != MOD_STATE_CLEAN) - { - saveBlock(block); - saved_blocks_count++; - } - // Delete from memory - sector->deleteBlock(block); - deleted_blocks_count++; - } - else - { - all_blocks_deleted = false; - } - } - - if(all_blocks_deleted) - { - sector_deletion_queue.push_back(si.getNode()->getKey()); - } - } - - deleteSectors(sector_deletion_queue); - - infostream<<"Map: Unloaded "< & modified_blocks) -{ - INodeDefManager *nodemgr = m_gamedef->ndef(); - - DSTACK(__FUNCTION_NAME); - //TimeTaker timer("transformLiquids()"); - - u32 loopcount = 0; - u32 initial_size = m_transforming_liquid.size(); - - u8 relax = g_settings->getS16("liquid_relax"); - bool fast_flood = g_settings->getS16("liquid_fast_flood"); - int water_level = g_settings->getS16("water_level"); - - // list of nodes that due to viscosity have not reached their max level height - UniqueQueue must_reflow, must_reflow_second; - - // List of MapBlocks that will require a lighting update (due to lava) - std::map lighting_modified_blocks; - - u16 loop_max = g_settings->getU16("liquid_loop_max"); - - //if (m_transforming_liquid.size() > 0) errorstream << "Liquid queue size="< 0) - { - // This should be done here so that it is done when continue is used - if (loopcount >= initial_size || loopcount >= loop_max) - break; - loopcount++; - /* - Get a queued transforming liquid node - */ - v3s16 p0 = m_transforming_liquid.pop_front(); - u16 total_level = 0; - // surrounding flowing liquid nodes - NodeNeighbor neighbors[7]; - // current level of every block - s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; - // target levels - s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; - s8 can_liquid_same_level = 0; - content_t liquid_kind = CONTENT_IGNORE; - content_t liquid_kind_flowing = CONTENT_IGNORE; - /* - Collect information about the environment - */ - const v3s16 *dirs = g_7dirs; - for (u16 i = 0; i < 7; i++) { - NeighborType nt = NEIGHBOR_SAME_LEVEL; - switch (i) { - case D_TOP: - nt = NEIGHBOR_UPPER; - break; - case D_BOTTOM: - nt = NEIGHBOR_LOWER; - break; - } - v3s16 npos = p0 + dirs[i]; - - neighbors[i].n = getNodeNoEx(npos); - neighbors[i].t = nt; - neighbors[i].p = npos; - neighbors[i].l = 0; - neighbors[i].i = 0; - NodeNeighbor & nb = neighbors[i]; - - switch (nodemgr->get(nb.n.getContent()).liquid_type) { - case LIQUID_NONE: - if (nb.n.getContent() == CONTENT_AIR) { - liquid_levels[i] = 0; - nb.l = 1; - } - break; - case LIQUID_SOURCE: - // if this node is not (yet) of a liquid type, - // choose the first liquid type we encounter - if (liquid_kind_flowing == CONTENT_IGNORE) - liquid_kind_flowing = nodemgr->getId( - nodemgr->get(nb.n).liquid_alternative_flowing); - if (liquid_kind == CONTENT_IGNORE) - liquid_kind = nb.n.getContent(); - if (nb.n.getContent() == liquid_kind) { - liquid_levels[i] = nb.n.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE; - nb.l = 1; - nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK); - } - break; - case LIQUID_FLOWING: - // if this node is not (yet) of a liquid type, - // choose the first liquid type we encounter - if (liquid_kind_flowing == CONTENT_IGNORE) - liquid_kind_flowing = nb.n.getContent(); - if (liquid_kind == CONTENT_IGNORE) - liquid_kind = nodemgr->getId( - nodemgr->get(nb.n).liquid_alternative_source); - if (nb.n.getContent() == liquid_kind_flowing) { - liquid_levels[i] = nb.n.getLevel(nodemgr); //(nb.n.param2 & LIQUID_LEVEL_MASK); - nb.l = 1; - } - break; - } - - if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) - ++can_liquid_same_level; - if (liquid_levels[i] > 0) - total_level += liquid_levels[i]; - - /* - infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c=" - << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1=" - << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt=" - << nodemgr->get(nb.n.getContent()).liquid_type - //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing - << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] - << " tlevel=" << (int)total_level << " cansame=" - << (int)can_liquid_same_level << std::endl; - */ - } - - if (liquid_kind == CONTENT_IGNORE || - !neighbors[D_SELF].l || - total_level <= 0) - continue; - - // fill bottom block - if (neighbors[D_BOTTOM].l) { - liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? - LIQUID_LEVEL_SOURCE : total_level; - total_level -= liquid_levels_want[D_BOTTOM]; - } - - //relax up - if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 && - liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && - total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level- - (can_liquid_same_level - relax) && - can_liquid_same_level >= relax + 1) { - total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level; - } - - // prevent lakes in air above unloaded blocks - if (liquid_levels[D_TOP] == 0 && (p0.Y > water_level) && neighbors[D_BOTTOM].n.getContent() == CONTENT_IGNORE && !(loopcount % 3)) { - --total_level; - } - - // calculate self level 5 blocks - u8 want_level = - total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - ? LIQUID_LEVEL_SOURCE - : total_level / can_liquid_same_level; - total_level -= want_level * can_liquid_same_level; - - //relax down - if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 && - liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && - total_level <= (can_liquid_same_level - relax) && - can_liquid_same_level >= relax + 1) { - total_level = 0; - } - - for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level - if (!neighbors[ii].l) - continue; - liquid_levels_want[ii] = want_level; - if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0) { - if (loopcount % 3 || liquid_levels[ii] <= 0){ - if (liquid_levels[ii] > liquid_levels_want[ii]) { - ++liquid_levels_want[ii]; - --total_level; - } - } else if (neighbors[ii].l > 0){ - ++liquid_levels_want[ii]; - --total_level; - } - } - } - - for (u16 ii = 0; ii < 7; ++ii) { - if (total_level < 1) break; - if (liquid_levels_want[ii] >= 0 && - liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) { - ++liquid_levels_want[ii]; - --total_level; - } - } - - // fill top block if can - if (neighbors[D_TOP].l) { - liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? - LIQUID_LEVEL_SOURCE : total_level; - total_level -= liquid_levels_want[D_TOP]; - } - - for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization - if ( neighbors[ii].i || - (liquid_levels_want[ii] >= 0 && - (fast_flood && p0.Y < water_level && - (initial_size >= 1000 - && ii != D_TOP - && want_level >= LIQUID_LEVEL_SOURCE/4 - && can_liquid_same_level >= 5 - && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE)))) - liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE; - - /* - if (total_level > 0) //|| flowed != volume) - infostream <<" AFTER level=" << (int)total_level - //<< " flowed="<ndef(); +s32 Map::transforming_liquid_size() { + return m_transforming_liquid.size(); +} - DSTACK(__FUNCTION_NAME); +void Map::transformLiquids(std::map &modified_blocks, + ServerEnvironment *env) +{ + DSTACK(FUNCTION_NAME); //TimeTaker timer("transformLiquids()"); u32 loopcount = 0; @@ -2016,24 +651,42 @@ void Map::transformLiquids(std::map & modified_blocks) infostream<<"transformLiquids(): initial_size="< must_reflow; + std::deque must_reflow; + + std::vector > changed_nodes; - // List of MapBlocks that will require a lighting update (due to lava) - std::map lighting_modified_blocks; + u32 liquid_loop_max = g_settings->getS32("liquid_loop_max"); + u32 loop_max = liquid_loop_max; - u16 loop_max = g_settings->getU16("liquid_loop_max"); +#if 0 + + /* If liquid_loop_max is not keeping up with the queue size increase + * loop_max up to a maximum of liquid_loop_max * dedicated_server_step. + */ + if (m_transforming_liquid.size() > loop_max * 2) { + // "Burst" mode + float server_step = g_settings->getFloat("dedicated_server_step"); + if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step) + m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10; + } else { + m_transforming_liquid_loop_count_multiplier = 1.0; + } - while(m_transforming_liquid.size() != 0) + loop_max *= m_transforming_liquid_loop_count_multiplier; +#endif + + while (m_transforming_liquid.size() != 0) { // This should be done here so that it is done when continue is used - if(loopcount >= initial_size || loopcount >= loop_max) + if (loopcount >= initial_size || loopcount >= loop_max) break; loopcount++; /* Get a queued transforming liquid node */ - v3s16 p0 = m_transforming_liquid.pop_front(); + v3s16 p0 = m_transforming_liquid.front(); + m_transforming_liquid.pop_front(); MapNode n0 = getNodeNoEx(p0); @@ -2041,22 +694,29 @@ void Map::transformLiquids(std::map & modified_blocks) Collect information about current node */ s8 liquid_level = -1; + // The liquid node which will be placed there if + // the liquid flows into this node. content_t liquid_kind = CONTENT_IGNORE; - LiquidType liquid_type = nodemgr->get(n0).liquid_type; + // The node which will be placed there if liquid + // can't flow into this node. + content_t floodable_node = CONTENT_AIR; + const ContentFeatures &cf = m_nodedef->get(n0); + LiquidType liquid_type = cf.liquid_type; switch (liquid_type) { case LIQUID_SOURCE: liquid_level = LIQUID_LEVEL_SOURCE; - liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing); + liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing); break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); liquid_kind = n0.getContent(); break; case LIQUID_NONE: - // if this is an air node, it *could* be transformed into a liquid. otherwise, - // continue with the next node. - if (n0.getContent() != CONTENT_AIR) + // if this node is 'floodable', it *could* be transformed + // into a liquid, otherwise, continue with the next node. + if (!cf.floodable) continue; + floodable_node = n0.getContent(); liquid_kind = CONTENT_AIR; break; } @@ -2074,6 +734,7 @@ void Map::transformLiquids(std::map & modified_blocks) NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid int num_neutrals = 0; bool flowing_down = false; + bool ignored_sources = false; for (u16 i = 0; i < 6; i++) { NeighborType nt = NEIGHBOR_SAME_LEVEL; switch (i) { @@ -2085,10 +746,11 @@ void Map::transformLiquids(std::map & modified_blocks) break; } v3s16 npos = p0 + dirs[i]; - NodeNeighbor nb = {getNodeNoEx(npos), nt, npos}; - switch (nodemgr->get(nb.n.getContent()).liquid_type) { + NodeNeighbor nb(getNodeNoEx(npos), nt, npos); + const ContentFeatures &cfnb = m_nodedef->get(nb.n); + switch (m_nodedef->get(nb.n.getContent()).liquid_type) { case LIQUID_NONE: - if (nb.n.getContent() == CONTENT_AIR) { + if (cfnb.floodable) { airs[num_airs++] = nb; // if the current node is a water source the neighbor // should be enqueded for transformation regardless of whether the @@ -2096,18 +758,26 @@ void Map::transformLiquids(std::map & modified_blocks) if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE) m_transforming_liquid.push_back(npos); // if the current node happens to be a flowing node, it will start to flow down here. - if (nb.t == NEIGHBOR_LOWER) { + if (nb.t == NEIGHBOR_LOWER) flowing_down = true; - } } else { neutrals[num_neutrals++] = nb; + if (nb.n.getContent() == CONTENT_IGNORE) { + // If node below is ignore prevent water from + // spreading outwards and otherwise prevent from + // flowing away as ignore node might be the source + if (nb.t == NEIGHBOR_LOWER) + flowing_down = true; + else + ignored_sources = true; + } } break; 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 = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing); - if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { // Do not count bottom source, it will screw things up @@ -2118,8 +788,8 @@ void Map::transformLiquids(std::map & modified_blocks) 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 = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing); - if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -2136,15 +806,28 @@ void Map::transformLiquids(std::map & modified_blocks) content_t new_node_content; s8 new_node_level = -1; s8 max_node_level = -1; - if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { + + u8 range = m_nodedef->get(liquid_kind).liquid_range; + if (range > LIQUID_LEVEL_MAX + 1) + range = LIQUID_LEVEL_MAX + 1; + + if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) // or the flowing alternative of the first of the surrounding sources (if it's air), so // it's perfectly safe to use liquid_kind here to determine the new node content. - new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source); + new_node_content = m_nodedef->getId(m_nodedef->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 = new_node_level = LIQUID_LEVEL_MAX; + if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range)) + new_node_content = liquid_kind; + else + new_node_content = floodable_node; + } else if (ignored_sources && liquid_level >= 0) { + // Maybe there are neighbouring sources that aren't loaded yet + // so prevent flowing away. + new_node_level = liquid_level; + new_node_content = liquid_kind; } else { // no surrounding sources, so get the maximum level that can flow into this node for (u16 i = 0; i < num_flows; i++) { @@ -2155,21 +838,21 @@ void Map::transformLiquids(std::map & modified_blocks) 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) + } else if (nb_liquid_level > max_node_level) { max_node_level = nb_liquid_level; + } break; case NEIGHBOR_LOWER: break; case NEIGHBOR_SAME_LEVEL: if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK && - nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) { + nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) max_node_level = nb_liquid_level - 1; - } break; } } - u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity; + u8 viscosity = m_nodedef->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 @@ -2182,24 +865,25 @@ void Map::transformLiquids(std::map & modified_blocks) new_node_level = liquid_level + 1; if (new_node_level != max_node_level) must_reflow.push_back(p0); - } else + } else { new_node_level = max_node_level; + } - u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1); - if (new_node_level >= (LIQUID_LEVEL_MAX+1-range)) + if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range)) new_node_content = liquid_kind; else - new_node_content = CONTENT_AIR; + new_node_content = floodable_node; } /* check if anything has changed. if not, just continue with the next node. */ - 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))) + if (new_node_content == n0.getContent() && + (m_nodedef->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))) continue; @@ -2208,22 +892,33 @@ void Map::transformLiquids(std::map & modified_blocks) */ MapNode n00 = n0; //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) { + if (m_nodedef->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 { // set the liquid level and flow bit to 0 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK); } + + // change the node. n0.setContent(new_node_content); + // on_flood() the node + if (floodable_node != CONTENT_AIR) { + if (env->getScriptIface()->node_on_flood(p0, n00, n0)) + continue; + } + + // Ignore light (because calling voxalgo::update_lighting_nodes) + n0.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); + // Find out whether there is a suspect for this action std::string suspect; - if(m_gamedef->rollback()){ + if (m_gamedef->rollback()) suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1); - } - if(!suspect.empty()){ + if (m_gamedef->rollback() && !suspect.empty()) { // Blame suspect RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true); // Get old node for rollback @@ -2242,18 +937,15 @@ void Map::transformLiquids(std::map & modified_blocks) v3s16 blockpos = getNodeBlockPos(p0); MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block != NULL) { + if (block != NULL) { modified_blocks[blockpos] = block; - // If new or old node emits light, MapBlock requires lighting update - if(nodemgr->get(n0).light_source != 0 || - nodemgr->get(n00).light_source != 0) - lighting_modified_blocks[block->getPos()] = block; + changed_nodes.push_back(std::pair(p0, n00)); } /* enqueue neighbors for update if neccessary */ - switch (nodemgr->get(n0.getContent()).liquid_type) { + switch (m_nodedef->get(n0.getContent()).liquid_type) { case LIQUID_SOURCE: case LIQUID_FLOWING: // make sure source flows into all neighboring nodes @@ -2272,12 +964,104 @@ void Map::transformLiquids(std::map & modified_blocks) } } //infostream<<"Map::transformLiquids(): loopcount="< 0) - m_transforming_liquid.push_back(must_reflow.pop_front()); - updateLighting(lighting_modified_blocks, modified_blocks); + + for (std::deque::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) + m_transforming_liquid.push_back(*iter); + + voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks); + + + /* ---------------------------------------------------------------------- + * Manage the queue so that it does not grow indefinately + */ + u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time"); + + if (time_until_purge == 0) + return; // Feature disabled + + time_until_purge *= 1000; // seconds -> milliseconds + + u64 curr_time = porting::getTimeMs(); + u32 prev_unprocessed = m_unprocessed_count; + m_unprocessed_count = m_transforming_liquid.size(); + + // if unprocessed block count is decreasing or stable + if (m_unprocessed_count <= prev_unprocessed) { + m_queue_size_timer_started = false; + } else { + if (!m_queue_size_timer_started) + m_inc_trending_up_start_time = curr_time; + m_queue_size_timer_started = true; + } + + // Account for curr_time overflowing + if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time) + m_queue_size_timer_started = false; + + /* If the queue has been growing for more than liquid_queue_purge_time seconds + * and the number of unprocessed blocks is still > liquid_loop_max then we + * cannot keep up; dump the oldest blocks from the queue so that the queue + * has liquid_loop_max items in it + */ + if (m_queue_size_timer_started + && curr_time - m_inc_trending_up_start_time > time_until_purge + && m_unprocessed_count > liquid_loop_max) { + + size_t dump_qty = m_unprocessed_count - liquid_loop_max; + + infostream << "transformLiquids(): DUMPING " << dump_qty + << " blocks from the queue" << std::endl; + + while (dump_qty--) + m_transforming_liquid.pop_front(); + + m_queue_size_timer_started = false; // optimistically assume we can keep up now + m_unprocessed_count = m_transforming_liquid.size(); + } +} + +std::vector Map::findNodesWithMetadata(v3s16 p1, v3s16 p2) +{ + std::vector positions_with_meta; + + sortBoxVerticies(p1, p2); + v3s16 bpmin = getNodeBlockPos(p1); + v3s16 bpmax = getNodeBlockPos(p2); + + VoxelArea area(p1, p2); + + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) + for (s16 x = bpmin.X; x <= bpmax.X; x++) { + v3s16 blockpos(x, y, z); + + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if (!block) { + verbosestream << "Map::getNodeMetadata(): Need to emerge " + << PP(blockpos) << std::endl; + block = emergeBlock(blockpos, false); + } + if (!block) { + infostream << "WARNING: Map::getNodeMetadata(): Block not found" + << std::endl; + continue; + } + + v3s16 p_base = blockpos * MAP_BLOCKSIZE; + std::vector keys = block->m_node_metadata.getAllKeys(); + for (size_t i = 0; i != keys.size(); i++) { + v3s16 p(keys[i] + p_base); + if (!area.contains(p)) + continue; + + positions_with_meta.push_back(p); + } + } + + return positions_with_meta; } -NodeMetadata* Map::getNodeMetadata(v3s16 p) +NodeMetadata *Map::getNodeMetadata(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE; @@ -2287,9 +1071,8 @@ NodeMetadata* Map::getNodeMetadata(v3s16 p) <m_node_metadata.set(p_rel, meta); + return true; } void Map::removeNodeMetadata(v3s16 p) @@ -2323,7 +1106,7 @@ void Map::removeNodeMetadata(v3s16 p) MapBlock *block = getBlockNoCreateNoEx(blockpos); if(block == NULL) { - infostream<<"WARNING: Map::removeNodeMetadata(): Block not found" + warningstream<<"Map::removeNodeMetadata(): Block not found" <m_node_timers.get(p_rel); - return t; + NodeTimer nt(t.timeout, t.elapsed, p); + return nt; } -void Map::setNodeTimer(v3s16 p, NodeTimer t) +void Map::setNodeTimer(const NodeTimer &t) { + v3s16 p = t.position; v3s16 blockpos = getNodeBlockPos(p); v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE; MapBlock *block = getBlockNoCreateNoEx(blockpos); @@ -2360,13 +1144,13 @@ void Map::setNodeTimer(v3s16 p, NodeTimer t) <m_node_timers.set(p_rel, t); + NodeTimer nt(t.timeout, t.elapsed, p_rel); + block->m_node_timers.set(nt); } void Map::removeNodeTimer(v3s16 p) @@ -2376,65 +1160,93 @@ void Map::removeNodeTimer(v3s16 p) MapBlock *block = getBlockNoCreateNoEx(blockpos); if(block == NULL) { - infostream<<"WARNING: Map::removeNodeTimer(): Block not found" + warningstream<<"Map::removeNodeTimer(): Block not found" <m_node_timers.remove(p_rel); } -s16 Map::getHeat(v3s16 p) +bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, + float start_off, float end_off, u32 needed_count) { - MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p)); - if(block != NULL) { - return block->heat; + 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; sget(n); + if(f.drawtype == NDT_NORMAL){ + // not transparent, see ContentFeature::updateTextures + count++; + if(count >= needed_count) + return true; + } + step *= stepfac; } - //errorstream << "No heat for " << p.X<<"," << p.Z << std::endl; - return 0; + return false; } -s16 Map::getHumidity(v3s16 p) -{ - MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p)); - if(block != NULL) { - return block->humidity; - } - //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl; - return 0; +bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) { + 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; + // The occlusion search of 'isOccluded()' must stop short of the target + // point by distance 'endoff' (end offset) to not enter the target mapblock. + // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal + // of a mapblock, because we must consider all view angles. + // sqrt(1^2 + 1^2 + 1^2) = 1.732 + float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569; + v3s16 spn = cam_pos_nodes; + s16 bs2 = MAP_BLOCKSIZE / 2 + 1; + // to reduce the likelihood of falsely occluded blocks + // require at least two solid blocks + // this is a HACK, we should think of a more precise algorithm + u32 needed_count = 2; + + return ( + // For the central point of the mapblock 'endoff' can be halved + isOccluded(spn, cpn, + step, stepfac, startoff, endoff / 2.0f, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count)); } /* ServerMap */ -ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge): +ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, + EmergeManager *emerge): Map(dout_server, gamedef), - m_seed(0), + settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), + m_emerge(emerge), m_map_metadata_changed(true) { - verbosestream<<__FUNCTION_NAME<getParamsFromSettings(g_settings); - if (!m_mgparams) - m_mgparams = new MapgenV6Params(); - - m_seed = m_mgparams->seed; - - 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)); - m_mgparams->seed = m_seed; - } + verbosestream<map_settings_mgr = &settings_mgr; /* Try to load map; if not found, create a new one. @@ -2446,21 +1258,13 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer bool succeeded = conf.readConfigFile(conf_path.c_str()); if (!succeeded || !conf.exists("backend")) { // fall back to sqlite3 - dbase = new Database_SQLite3(this, savedir); conf.set("backend", "sqlite3"); - } else { - std::string backend = conf.get("backend"); - if (backend == "dummy") - dbase = new Database_Dummy(this); - else if (backend == "sqlite3") - dbase = new Database_SQLite3(this, savedir); - #if USE_LEVELDB - else if (backend == "leveldb") - dbase = new Database_LevelDB(this, savedir); - #endif - else - throw BaseException("Unknown map backend"); } + std::string backend = conf.get("backend"); + dbase = createDatabase(backend, savedir, conf); + + if (!conf.updateConfigFile(conf_path.c_str())) + errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl; m_savedir = savedir; m_map_saving_enabled = false; @@ -2479,26 +1283,15 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer } else { - try{ - // Load map metadata (seed, chunksize) - loadMapMeta(); - } - catch(SettingNotFoundException &e){ - infostream<<"ServerMap: Some metadata not found." - <<" Using default settings."<seed; +} + +s16 ServerMap::getWaterLevel() +{ + return getMapgenParams()->water_level; +} + +bool ServerMap::saoPositionOverLimit(const v3f &p) +{ + return getMapgenParams()->saoPosOverLimit(p); +} - delete m_mgparams; +bool ServerMap::blockpos_over_mapgen_limit(v3s16 p) +{ + const s16 mapgen_limit_bp = rangelim( + getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) / + MAP_BLOCKSIZE; + return p.X < -mapgen_limit_bp || + p.X > mapgen_limit_bp || + p.Y < -mapgen_limit_bp || + p.Y > mapgen_limit_bp || + p.Z < -mapgen_limit_bp || + p.Z > mapgen_limit_bp; } -bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) +bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) { - bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info; - EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos)); - - s16 chunksize = m_mgparams->chunksize; - s16 coffset = -chunksize / 2; - v3s16 chunk_offset(coffset, coffset, coffset); - 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); - - // Do nothing if not inside limits (+-1 because of neighbors) - if(blockpos_over_limit(blockpos_min - extra_borders) || - blockpos_over_limit(blockpos_max + extra_borders)) + s16 csize = getMapgenParams()->chunksize; + v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize); + v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1); + + bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; + EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax)); + + v3s16 extra_borders(1, 1, 1); + v3s16 full_bpmin = bpmin - extra_borders; + v3s16 full_bpmax = bpmax + extra_borders; + + // Do nothing if not inside mapgen limits (+-1 because of neighbors) + if (blockpos_over_mapgen_limit(full_bpmin) || + blockpos_over_mapgen_limit(full_bpmax)) return false; - data->seed = m_seed; - data->blockpos_min = blockpos_min; - data->blockpos_max = blockpos_max; + data->seed = getSeed(); + data->blockpos_min = bpmin; + data->blockpos_max = bpmax; data->blockpos_requested = blockpos; - data->nodedef = m_gamedef->ndef(); + data->nodedef = m_nodedef; /* Create the whole area of this and the neighboring blocks */ - { - //TimeTaker timer("initBlockMake() create area"); - - 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(x, z); - // Sector metadata is loaded from disk if not already loaded. - ServerMapSector *sector = createSector(sectorpos); - assert(sector); - - for(s16 y=blockpos_min.Y-extra_borders.Y; - y<=blockpos_max.Y+extra_borders.Y; y++) - { - v3s16 p(x,y,z); - //MapBlock *block = createBlock(p); - // 1) get from memory, 2) load from disk - MapBlock *block = emergeBlock(p, false); - // 3) create a blank one - if(block == NULL) - { - block = createBlock(p); + for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++) + for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) { + v2s16 sectorpos(x, z); + // Sector metadata is loaded from disk if not already loaded. + ServerMapSector *sector = createSector(sectorpos); + FATAL_ERROR_IF(sector == NULL, "createSector() failed"); - /* - Block gets sunlight if this is true. + for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) { + v3s16 p(x, y, z); - Refer to the map generator heuristics. - */ - bool ug = m_emerge->isBlockUnderground(p); - block->setIsUnderground(ug); - } + MapBlock *block = emergeBlock(p, false); + if (block == NULL) { + block = createBlock(p); - // Lighting will not be valid after make_chunk is called - block->setLightingExpired(true); - // Lighting will be calculated - //block->setLightingExpired(false); + // Block gets sunlight if this is true. + // Refer to the map generator heuristics. + bool ug = m_emerge->isBlockUnderground(p); + block->setIsUnderground(ug); } } } @@ -2649,21 +1452,14 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) neighboring blocks */ - // The area that contains this block and it's neighbors - v3s16 bigarea_blocks_min = blockpos_min - extra_borders; - v3s16 bigarea_blocks_max = blockpos_max + extra_borders; + data->vmanip = new MMVManip(this); + data->vmanip->initialEmerge(full_bpmin, full_bpmax); - data->vmanip = new ManualMapVoxelManipulator(this); - //data->vmanip->setMap(this); - - // Add the area - { - //TimeTaker timer("initBlockMake() initialEmerge"); - data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false); - } - - // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE -/* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) { + // Note: we may need this again at some point. +#if 0 + // Ensure none of the blocks to be generated were marked as + // containing CONTENT_IGNORE + for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) { for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) { for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) { core::map::Node *n; @@ -2675,123 +1471,47 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) n->setValue(flags); } } - }*/ + } +#endif // Data is ready now. return true; } -MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, - std::map &changed_blocks) +void ServerMap::finishBlockMake(BlockMakeData *data, + std::map *changed_blocks) { - v3s16 blockpos_min = data->blockpos_min; - v3s16 blockpos_max = data->blockpos_max; - v3s16 blockpos_requested = data->blockpos_requested; - /*infostream<<"finishBlockMake(): ("<mapgen_debug_info; - - /*infostream<<"Resulting vmanip:"<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); - } + v3s16 bpmin = data->blockpos_min; + v3s16 bpmax = data->blockpos_max; + + v3s16 extra_borders(1, 1, 1); + + bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; + EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax)); /* Blit generated stuff to map NOTE: blitBackAll adds nearly everything to changed_blocks */ - { - // 70ms @cs=8 - //TimeTaker timer("finishBlockMake() blitBackAll"); - data->vmanip->blitBackAll(&changed_blocks); - } + data->vmanip->blitBackAll(changed_blocks); - EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size()); + EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" + << changed_blocks->size()); /* Copy transforming liquid information */ - while(data->transforming_liquid.size() > 0) - { - v3s16 p = data->transforming_liquid.pop_front(); - m_transforming_liquid.push_back(p); - } - - /* - Do stuff in central blocks - */ - - /* - Update lighting - */ - { -#if 0 - TimeTaker t("finishBlockMake lighting update"); - - core::map lighting_update_blocks; - - // 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); - } - - 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=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 + while (data->transforming_liquid.size()) { + m_transforming_liquid.push_back(data->transforming_liquid.front()); + data->transforming_liquid.pop_front(); } - /* - Go through changed blocks - */ - for(std::map::iterator i = changed_blocks.begin(); - i != changed_blocks.end(); ++i) - { - MapBlock *block = i->second; - assert(block); + for (std::map::iterator + it = changed_blocks->begin(); + it != changed_blocks->end(); ++it) { + MapBlock *block = it->second; + if (!block) + continue; /* Update day/night difference cache of the MapBlocks */ @@ -2800,19 +1520,19 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, Set block as modified */ block->raiseModified(MOD_STATE_WRITE_NEEDED, - "finishBlockMake expireDayNightDiff"); + MOD_REASON_EXPIRE_DAYNIGHTDIFF); } /* Set central blocks as generated */ - 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); + for (s16 x = bpmin.X; x <= bpmax.X; x++) + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) { + MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z)); + if (!block) + continue; + block->setGenerated(true); } @@ -2821,75 +1541,12 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, NOTE: Will be saved later. */ //save(MOD_STATE_WRITE_AT_UNLOAD); - - /*infostream<<"finishBlockMake() done for ("<getEnv(); - if (senv->m_use_weather) { - 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); - updateBlockHeat(senv, p * MAP_BLOCKSIZE, NULL); - updateBlockHumidity(senv, p * MAP_BLOCKSIZE, NULL); - } - } else { - 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++) - { - MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z)); - block->heat = HEAT_UNDEFINED; - block->humidity = HUMIDITY_UNDEFINED; - block->weather_update_time = 0; - } - } - -#if 0 - if(enable_mapgen_debug_info) - { - /* - Analyze resulting blocks - */ - /*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); - infostream<<"Generated "< MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - throw InvalidPositionException("createSector(): pos. over limit"); + const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE; + if (p2d.X < -max_limit_bp || + p2d.X > max_limit_bp || + p2d.Y < -max_limit_bp || + p2d.Y > max_limit_bp) + throw InvalidPositionException("createSector(): pos. over max mapgen limit"); /* Generate blank sector @@ -2954,7 +1613,7 @@ MapBlock * ServerMap::generateBlock( std::map &modified_blocks ) { - DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z); + DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z); /*infostream<<"generateBlock(): " <<"("< 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("createBlock(): pos. over limit"); + if (blockpos_over_max_limit(p)) + throw InvalidPositionException("createBlock(): pos. over max mapgen limit"); v2s16 p2d(p.X, p.Z); s16 block_y = p.Y; @@ -3087,7 +1741,7 @@ MapBlock * ServerMap::createBlock(v3s16 p) lighting on blocks for them. */ ServerMapSector *sector; - try{ + try { sector = (ServerMapSector*)createSector(p2d); assert(sector->getId() == MAPSECTOR_SERVER); } @@ -3128,7 +1782,7 @@ MapBlock * ServerMap::createBlock(v3s16 p) MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) { DSTACKF("%s: p=(%d,%d,%d), create_blank=%d", - __FUNCTION_NAME, + FUNCTION_NAME, p.X, p.Y, p.Z, create_blank); { @@ -3149,7 +1803,9 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) return block; } - /*if(allow_generate) + +#if 0 + if(allow_generate) { std::map modified_blocks; MapBlock *block = generateBlock(p, modified_blocks); @@ -3172,11 +1828,43 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) return block; } - }*/ + } +#endif return NULL; } +MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d) +{ + MapBlock *block = getBlockNoCreateNoEx(p3d); + if (block == NULL) + m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false); + + return block; +} + +// N.B. This requires no synchronization, since data will not be modified unless +// the VoxelManipulator being updated belongs to the same thread. +void ServerMap::updateVManip(v3s16 pos) +{ + Mapgen *mg = m_emerge->getCurrentMapgen(); + if (!mg) + return; + + MMVManip *vm = mg->vm; + if (!vm) + return; + + if (!vm->m_area.contains(pos)) + return; + + s32 idx = vm->m_area.index(pos); + vm->m_data[idx] = getNodeNoEx(pos); + vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA; + + vm->m_is_dirty = true; +} + s16 ServerMap::findGroundLevel(v2s16 p2d) { #if 0 @@ -3222,7 +1910,8 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) } bool ServerMap::loadFromFolders() { - if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ? + if (!dbase->initialized() && + !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) return true; return false; } @@ -3231,7 +1920,7 @@ void ServerMap::createDirs(std::string path) { if(fs::CreateAllDirs(path) == false) { - m_dout<::iterator i = m_sectors.begin(); - i != m_sectors.end(); ++i) - { + i != m_sectors.end(); ++i) { ServerMapSector *sector = (ServerMapSector*)i->second; assert(sector->getId() == MAPSECTOR_SERVER); - if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) - { + if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) { saveSectorMeta(sector); sector_meta_count++; } - std::list blocks; + + MapBlockVect blocks; sector->getBlocks(blocks); - for(std::list::iterator j = blocks.begin(); - j != blocks.end(); ++j) - { + for(MapBlockVect::iterator j = blocks.begin(); + j != blocks.end(); ++j) { MapBlock *block = *j; block_count_all++; - if(block->getModified() >= (u32)save_level) - { + if(block->getModified() >= (u32)save_level) { // Lazy beginSave() - if(!save_started){ + if(!save_started) { beginSave(); save_started = true; } - modprofiler.add(block->getModifiedReason(), 1); + modprofiler.add(block->getModifiedReasonString(), 1); saveBlock(block); block_count++; @@ -3379,6 +2066,7 @@ void ServerMap::save(ModifiedState save_level) } } } + if(save_started) endSave(); @@ -3386,136 +2074,48 @@ void ServerMap::save(ModifiedState save_level) Only print if something happened or saved whole map */ if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0 - || block_count != 0) - { + || block_count != 0) { infostream<<"ServerMap: Written: " < &dst) -{ - if(loadFromFolders()){ - errorstream<<"Map::listAllLoadableBlocks(): Result will be missing " - <<"all blocks that are stored in flat files"<listAllLoadableBlocks(dst); -} - -void ServerMap::listAllLoadedBlocks(std::list &dst) -{ - for(std::map::iterator si = m_sectors.begin(); - si != m_sectors.end(); ++si) - { - MapSector *sector = si->second; - - std::list blocks; - sector->getBlocks(blocks); - - for(std::list::iterator i = blocks.begin(); - i != blocks.end(); ++i) - { - MapBlock *block = (*i); - v3s16 p = block->getPos(); - dst.push_back(p); - } + <<", "< &dst) { - DSTACK(__FUNCTION_NAME); - - /*infostream<<"ServerMap::saveMapMeta(): " - <<"seed="<setParamsToSettings(¶ms); - params.writeLines(ss); - - ss<<"[end_of_params]\n"; - - if(!fs::safeWriteToFile(fullpath, ss.str())) - { - infostream<<"ERROR: ServerMap::saveMapMeta(): " - <<"could not write "<listAllLoadableBlocks(dst); } -void ServerMap::loadMapMeta() +void ServerMap::listAllLoadedBlocks(std::vector &dst) { - DSTACK(__FUNCTION_NAME); - - /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata" - <::iterator si = m_sectors.begin(); + si != m_sectors.end(); ++si) { - infostream<<"ERROR: ServerMap::loadMapMeta(): " - <<"could not open"<second; - Settings params; + MapBlockVect blocks; + sector->getBlocks(blocks); - for(;;) - { - if(is.eof()) - throw SerializationError - ("ServerMap::loadMapMeta(): [end_of_params] not found"); - std::string line; - std::getline(is, line); - std::string trimmedline = trim(line); - if(trimmedline == "[end_of_params]") - break; - params.parseConfigLine(line); - } - - MapgenParams *mgparams; - try { - mgparams = m_emerge->getParamsFromSettings(¶ms); - } catch (SettingNotFoundException &e) { - infostream << "Couldn't get a setting from map_meta.txt: " - << e.what() << std::endl; - mgparams = NULL; - } - - if (mgparams) { - if (m_mgparams) - delete m_mgparams; - m_mgparams = mgparams; - m_seed = mgparams->seed; - } else { - if (params.exists("seed")) { - m_seed = params.getU64("seed"); - m_mgparams->seed = m_seed; + for(MapBlockVect::iterator i = blocks.begin(); + i != blocks.end(); ++i) { + v3s16 p = (*i)->getPos(); + dst.push_back(p); } } - - verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<beginSave(); } -void ServerMap::endSave() { +void ServerMap::endSave() +{ dbase->endSave(); } -void ServerMap::saveBlock(MapBlock *block) +bool ServerMap::saveBlock(MapBlock *block) { - dbase->saveBlock(block); + return saveBlock(block, dbase); } -void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load) +bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db) { - DSTACK(__FUNCTION_NAME); + v3s16 p3d = block->getPos(); - std::string fullpath = sectordir+DIR_DELIM+blockfile; - try{ + // Dummy blocks are not written + if (block->isDummy()) { + warningstream << "saveBlock: Not writing dummy block " + << PP(p3d) << std::endl; + return true; + } + + // Format used for writing + u8 version = SER_FMT_VER_HIGHEST_WRITE; + + /* + [0] u8 serialization version + [1] data + */ + std::ostringstream o(std::ios_base::binary); + o.write((char*) &version, 1); + block->serialize(o, version, true); + + std::string data = o.str(); + bool ret = db->saveBlock(p3d, data); + if (ret) { + // We just wrote it to the disk so clear modified flag + block->resetModified(); + } + return ret; +} + +void ServerMap::loadBlock(const std::string §ordir, const std::string &blockfile, + MapSector *sector, bool save_after_load) +{ + DSTACK(FUNCTION_NAME); + std::string fullpath = sectordir + DIR_DELIM + blockfile; + try { std::ifstream is(fullpath.c_str(), std::ios_base::binary); - if(is.good() == false) + if (!is.good()) throw FileNotGoodException("Cannot open block file"); v3s16 p3d = getBlockPos(sectordir, blockfile); @@ -3746,8 +2405,11 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto block->deSerialize(is, version, true); // If it's a new block, insert it to the map - if(created_new) + if (created_new) { sector->insertBlock(block); + ReflowScan scanner(this, m_emerge->ndef); + scanner.scan(block, &m_transforming_liquid); + } /* Save blocks loaded in old format in new format @@ -3767,13 +2429,13 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto } catch(SerializationError &e) { - infostream<<"WARNING: Invalid block data on disk " + warningstream<<"Invalid block data on disk " <<"fullpath="< 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); @@ -3813,8 +2468,11 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool block->deSerialize(is, version, true); // If it's a new block, insert it to the map - if(created_new) + if (created_new) { sector->insertBlock(block); + ReflowScan scanner(this, m_emerge->ndef); + scanner.scan(block, &m_transforming_liquid); + } /* Save blocks loaded in old format in new format @@ -3827,7 +2485,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool // We just loaded it from, so it's up-to-date. block->resetModified(); - } catch(SerializationError &e) { @@ -3843,302 +2500,136 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool <<"(ignore_world_load_errors)"<loadBlock(blockpos); - if (ret) return (ret); - // Not found in database, try the files + v2s16 p2d(blockpos.X, blockpos.Z); - // The directory layout we're going to load from. - // 1 - original sectors/xxxxzzzz/ - // 2 - new sectors2/xxx/zzz/ - // If we load from anything but the latest structure, we will - // immediately save to the new one, and remove the old. - int loadlayout = 1; - std::string sectordir1 = getSectorDir(p2d, 1); - std::string sectordir; - if(fs::PathExists(sectordir1)) - { - sectordir = sectordir1; - } - else - { - loadlayout = 2; - sectordir = getSectorDir(p2d, 2); - } + std::string ret; + dbase->loadBlock(blockpos, &ret); + if (ret != "") { + loadBlock(&ret, blockpos, createSector(p2d), false); + } else { + // 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/ + // If we load from anything but the latest structure, we will + // immediately save to the new one, and remove the old. + int loadlayout = 1; + std::string sectordir1 = getSectorDir(p2d, 1); + std::string sectordir; + if (fs::PathExists(sectordir1)) { + sectordir = sectordir1; + } else { + loadlayout = 2; + sectordir = getSectorDir(p2d, 2); + } - /* + /* Make sure sector is loaded - */ - MapSector *sector = getSectorNoGenerateNoEx(p2d); - if(sector == NULL) - { - try{ - sector = loadSectorMeta(sectordir, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - return NULL; - } - catch(FileNotGoodException &e) - { - return NULL; - } - catch(std::exception &e) - { - return NULL; + */ + + MapSector *sector = getSectorNoGenerateNoEx(p2d); + if (sector == NULL) { + try { + sector = loadSectorMeta(sectordir, loadlayout != 2); + } catch(InvalidFilenameException &e) { + return NULL; + } catch(FileNotGoodException &e) { + return NULL; + } catch(std::exception &e) { + return NULL; + } } - } - /* + + /* Make sure file exists - */ + */ - std::string blockfilename = getBlockFilename(blockpos); - if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false) - return NULL; + std::string blockfilename = getBlockFilename(blockpos); + if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) + return NULL; - /* + /* Load block and save it to the database - */ - loadBlock(sectordir, blockfilename, sector, true); - return getBlockNoCreateNoEx(blockpos); -} - -void ServerMap::PrintInfo(std::ostream &out) -{ - out<<"ServerMap: "; -} - -s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3s16 p, MapBlock *block) -{ - u32 gametime = env->getGameTime(); - - if (block) { - if (gametime - block->weather_update_time < 10) - return block->heat; - } else { - block = getBlockNoCreateNoEx(getNodeBlockPos(p)); - } - - f32 heat = m_emerge->biomedef->calcBlockHeat(p, m_seed, - env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed()); - - if(block) { - block->heat = heat; - block->weather_update_time = gametime; - } - return heat; -} - -s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block) -{ - u32 gametime = env->getGameTime(); - - if (block) { - if (gametime - block->weather_update_time < 10) - return block->humidity; - } else { - block = getBlockNoCreateNoEx(getNodeBlockPos(p)); + */ + loadBlock(sectordir, blockfilename, sector, true); } - - f32 humidity = m_emerge->biomedef->calcBlockHumidity(p, m_seed, - env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed()); - - if(block) { - block->humidity = humidity; - block->weather_update_time = gametime; + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if (created_new && (block != NULL)) { + std::map modified_blocks; + // Fix lighting if necessary + voxalgo::update_block_border_lighting(this, block, modified_blocks); + if (!modified_blocks.empty()) { + //Modified lighting, send event + MapEditEvent event; + event.type = MEET_OTHER; + std::map::iterator it; + for (it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + dispatchEvent(&event); + } } - return humidity; -} - -/* - MapVoxelManipulator -*/ - -MapVoxelManipulator::MapVoxelManipulator(Map *map) -{ - m_map = map; -} - -MapVoxelManipulator::~MapVoxelManipulator() -{ - /*infostream<<"MapVoxelManipulator: blocks: "<::iterator n; - n = m_loaded_blocks.find(p); - if(n != m_loaded_blocks.end()) - continue; - - bool block_data_inexistent = false; - try - { - TimeTaker timer1("emerge load", &emerge_load_time); - - /*infostream<<"Loading block (caller_id="<getBlockNoCreate(p); - if(block->isDummy()) - block_data_inexistent = true; - else - block->copyTo(*this); - } - catch(InvalidPositionException &e) - { - block_data_inexistent = true; - } - - if(block_data_inexistent) - { - flags |= VMANIP_BLOCK_DATA_INEXIST; - - VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); - // Fill with VOXELFLAG_INEXISTENT - for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) - for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++) - { - s32 i = m_area.index(a.MinEdge.X,y,z); - memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE); - } - } - /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE) - { - // Mark that block was loaded as blank - flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; - }*/ + if (!dbase->deleteBlock(blockpos)) + return false; - m_loaded_blocks[p] = flags; + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if (block) { + v2s16 p2d(blockpos.X, blockpos.Z); + MapSector *sector = getSectorNoGenerateNoEx(p2d); + if (!sector) + return false; + sector->deleteBlock(block); } - //infostream<<"emerge done"< & modified_blocks) +void ServerMap::PrintInfo(std::ostream &out) { - if(m_area.getExtent() == v3s16(0,0,0)) - return; - - //TimeTaker timer1("blitBack"); - - /*infostream<<"blitBack(): m_loaded_blocks.size()=" - <getBlockNoCreate(blockpos); - blockpos_last = blockpos; - block_checked_in_modified = false; - } - - // Calculate relative position in block - v3s16 relpos = p - blockpos * MAP_BLOCKSIZE; - - // Don't continue if nothing has changed here - if(block->getNode(relpos) == n) - continue; - - //m_map->setNode(m_area.MinEdge + p, n); - block->setNode(relpos, n); - - /* - Make sure block is in modified_blocks - */ - if(block_checked_in_modified == false) - { - modified_blocks[blockpos] = block; - block_checked_in_modified = true; - } - } - catch(InvalidPositionException &e) - { - } - } + out<<"ServerMap: "; } -ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map): - MapVoxelManipulator(map), - m_create_area(false) +bool ServerMap::repairBlockLight(v3s16 blockpos, + std::map *modified_blocks) { + MapBlock *block = emergeBlock(blockpos, false); + if (!block || !block->isGenerated()) + return false; + voxalgo::repair_block_light(this, block, modified_blocks); + return true; } -ManualMapVoxelManipulator::~ManualMapVoxelManipulator() +MMVManip::MMVManip(Map *map): + VoxelManipulator(), + m_is_dirty(false), + m_create_area(false), + m_map(map) { } -void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) +MMVManip::~MMVManip() { - // Just create the area so that it can be pointed to - VoxelManipulator::emerge(a, caller_id); } -void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min, - v3s16 blockpos_max, bool load_if_inexistent) +void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, + bool load_if_inexistent) { TimeTaker timer1("initialEmerge", &emerge_time); @@ -4190,27 +2681,26 @@ void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min, if(block_data_inexistent) { - - if (load_if_inexistent) { + + if (load_if_inexistent && !blockpos_over_max_limit(p)) { ServerMap *svrmap = (ServerMap *)m_map; block = svrmap->emergeBlock(p, false); if (block == NULL) block = svrmap->createBlock(p); - else - block->copyTo(*this); + block->copyTo(*this); } else { flags |= VMANIP_BLOCK_DATA_INEXIST; - + /* Mark area inexistent */ VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); - // Fill with VOXELFLAG_INEXISTENT + // Fill with VOXELFLAG_NO_DATA for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++) { s32 i = m_area.index(a.MinEdge.X,y,z); - memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE); + memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE); } } } @@ -4222,10 +2712,12 @@ void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min, m_loaded_blocks[p] = flags; } + + m_is_dirty = false; } -void ManualMapVoxelManipulator::blitBackAll( - std::map * modified_blocks) +void MMVManip::blitBackAll(std::map *modified_blocks, + bool overwrite_generated) { if(m_area.getExtent() == v3s16(0,0,0)) return; @@ -4240,10 +2732,9 @@ void ManualMapVoxelManipulator::blitBackAll( v3s16 p = i->first; MapBlock *block = m_map->getBlockNoCreateNoEx(p); bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST); - if(existed == false) - { + if ((existed == false) || (block == NULL) || + (overwrite_generated == false && block->isGenerated() == true)) continue; - } block->copyFrom(*this);