X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.cpp;h=7bc1334b0fbf9aa2238e7abfe29e2006e5c74628;hb=21df26984da91143c15587f5a03c98d68c3adc4e;hp=689112c7d3102b40c6a228a16e6b935243d53316;hpb=f8ad01ab7c4cf012781bd4caa821544e676c9267;p=dragonfireclient.git diff --git a/src/map.cpp b/src/map.cpp index 689112c7d..7bc1334b0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -37,23 +37,24 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "reflowscan.h" #include "emerge.h" -#include "mapgen_v6.h" -#include "mg_biome.h" +#include "mapgen/mapgen_v6.h" +#include "mapgen/mg_biome.h" #include "config.h" #include "server.h" -#include "database.h" -#include "database-dummy.h" -#include "database-sqlite3.h" +#include "database/database.h" +#include "database/database-dummy.h" +#include "database/database-sqlite3.h" +#include "script/scripting_server.h" #include #include #if USE_LEVELDB -#include "database-leveldb.h" +#include "database/database-leveldb.h" #endif #if USE_REDIS -#include "database-redis.h" +#include "database/database-redis.h" #endif #if USE_POSTGRESQL -#include "database-postgresql.h" +#include "database/database-postgresql.h" #endif @@ -61,15 +62,9 @@ with this program; if not, write to the Free Software Foundation, Inc., Map */ -Map::Map(std::ostream &dout, IGameDef *gamedef): - m_dout(dout), +Map::Map(IGameDef *gamedef): m_gamedef(gamedef), - 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_nodedef(gamedef->ndef()) { } @@ -78,10 +73,8 @@ Map::~Map() /* Free all MapSectors */ - for(std::map::iterator i = m_sectors.begin(); - i != m_sectors.end(); ++i) - { - delete i->second; + for (auto §or : m_sectors) { + delete sector.second; } } @@ -95,17 +88,14 @@ void Map::removeEventReceiver(MapEventReceiver *event_receiver) m_event_receivers.erase(event_receiver); } -void Map::dispatchEvent(MapEditEvent *event) +void Map::dispatchEvent(const MapEditEvent &event) { - for(std::set::iterator - i = m_event_receivers.begin(); - i != m_event_receivers.end(); ++i) - { - (*i)->onMapEditEvent(event); + for (MapEventReceiver *event_receiver : m_event_receivers) { + event_receiver->onMapEditEvent(event); } } -MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p) +MapSector * Map::getSectorNoGenerateNoLock(v2s16 p) { if(m_sector_cache != NULL && p == m_sector_cache_p){ MapSector * sector = m_sector_cache; @@ -114,7 +104,7 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p) std::map::iterator n = m_sectors.find(p); - if(n == m_sectors.end()) + if (n == m_sectors.end()) return NULL; MapSector *sector = n->second; @@ -126,24 +116,15 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p) return sector; } -MapSector * Map::getSectorNoGenerateNoEx(v2s16 p) -{ - return getSectorNoGenerateNoExNoLock(p); -} - MapSector * Map::getSectorNoGenerate(v2s16 p) { - MapSector *sector = getSectorNoGenerateNoEx(p); - if(sector == NULL) - throw InvalidPositionException(); - - return sector; + return getSectorNoGenerateNoLock(p); } MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d) { v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorNoGenerateNoEx(p2d); + MapSector * sector = getSectorNoGenerate(p2d); if(sector == NULL) return NULL; MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y); @@ -158,16 +139,18 @@ MapBlock * Map::getBlockNoCreate(v3s16 p3d) return block; } -bool Map::isNodeUnderground(v3s16 p) +void Map::listAllLoadedBlocks(std::vector &dst) { - v3s16 blockpos = getNodeBlockPos(p); - try{ - MapBlock * block = getBlockNoCreate(blockpos); - return block->getIsUnderground(); - } - catch(InvalidPositionException &e) - { - return false; + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; + + MapBlockVect blocks; + sector->getBlocks(blocks); + + for (MapBlock *block : blocks) { + v3s16 p = block->getPos(); + dst.push_back(p); + } } } @@ -179,14 +162,14 @@ bool Map::isValidPosition(v3s16 p) } // Returns a CONTENT_IGNORE node if not found -MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position) +MapNode Map::getNode(v3s16 p, bool *is_valid_position) { v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreateNoEx(blockpos); if (block == NULL) { if (is_valid_position != NULL) *is_valid_position = false; - return MapNode(CONTENT_IGNORE); + return {CONTENT_IGNORE}; } v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; @@ -197,24 +180,22 @@ MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position) return node; } -#if 0 -// Deprecated -// throws InvalidPositionException if not found -// TODO: Now this is deprecated, getNodeNoEx should be renamed -MapNode Map::getNode(v3s16 p) +static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n) { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *block = getBlockNoCreateNoEx(blockpos); - if (block == NULL) - throw InvalidPositionException(); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - bool is_valid_position; - MapNode node = block->getNodeNoCheck(relpos, &is_valid_position); - if (!is_valid_position) - throw InvalidPositionException(); - return node; + // Never allow placing CONTENT_IGNORE, it causes problems + if(n.getContent() == CONTENT_IGNORE){ + const NodeDefManager *nodedef = block->getParent()->getNodeDefManager(); + v3s16 blockpos = block->getPos(); + v3s16 p = blockpos * MAP_BLOCKSIZE + relpos; + bool temp_bool; + errorstream<<"Not allowing to place CONTENT_IGNORE" + <<" while trying to replace \"" + <get(block->getNodeNoCheck(relpos, &temp_bool)).name + <<"\" at "<setNodeNoCheck(relpos, n); } -#endif // throws InvalidPositionException if not found void Map::setNode(v3s16 p, MapNode & n) @@ -222,17 +203,7 @@ void Map::setNode(v3s16 p, MapNode & n) 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){ - bool temp_bool; - errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE" - <<" while trying to replace \"" - <get(block->getNodeNoCheck(relpos, &temp_bool)).name - <<"\" at "<setNodeNoCheck(relpos, n); + set_node_in_block(block, relpos, n); } void Map::addNodeAndUpdate(v3s16 p, MapNode n, @@ -242,8 +213,14 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // Collect old node for rollback RollbackNode rollback_oldnode(this, p, m_gamedef); + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreate(blockpos); + if (block->isDummy()) + throw InvalidPositionException(); + v3s16 relpos = p - blockpos * MAP_BLOCKSIZE; + // This is needed for updating the lighting - MapNode oldnode = getNodeNoEx(p); + MapNode oldnode = block->getNodeUnsafe(relpos); // Remove node metadata if (remove_metadata) { @@ -251,21 +228,29 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, } // 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); - - // Update lighting - std::vector > oldnodes; - oldnodes.push_back(std::pair(p, oldnode)); - voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks); - - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - i->second->expireDayNightDiff(); + const ContentFeatures &cf = m_nodedef->get(n); + const ContentFeatures &oldcf = m_nodedef->get(oldnode); + if (cf.lightingEquivalent(oldcf)) { + // No light update needed, just copy over the old light. + n.setLight(LIGHTBANK_DAY, oldnode.getLightRaw(LIGHTBANK_DAY, oldcf), cf); + n.setLight(LIGHTBANK_NIGHT, oldnode.getLightRaw(LIGHTBANK_NIGHT, oldcf), cf); + set_node_in_block(block, relpos, n); + + modified_blocks[blockpos] = block; + } else { + // Ignore light (because calling voxalgo::update_lighting_nodes) + n.setLight(LIGHTBANK_DAY, 0, cf); + n.setLight(LIGHTBANK_NIGHT, 0, cf); + set_node_in_block(block, relpos, n); + + // Update lighting + std::vector > oldnodes; + oldnodes.emplace_back(p, oldnode); + voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks); + + for (auto &modified_block : modified_blocks) { + modified_block.second->expireDayNightDiff(); + } } // Report for rollback @@ -276,31 +261,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, action.setSetNode(p, rollback_oldnode, rollback_newnode); m_gamedef->rollback()->reportAction(action); } - - /* - Add neighboring liquid nodes and this node to transform queue. - (it's vital for the node itself to get updated last, if it was removed.) - */ - v3s16 dirs[7] = { - 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 - v3s16(0,0,0), // self - }; - for(u16 i=0; i<7; i++) - { - v3s16 p2 = p + dirs[i]; - - 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); - } } void Map::removeNodeAndUpdate(v3s16 p, @@ -322,18 +282,15 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata) addNodeAndUpdate(p, n, modified_blocks, remove_metadata); // Copy modified_blocks to event - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - event.modified_blocks.insert(i->first); + for (auto &modified_block : modified_blocks) { + event.modified_blocks.insert(modified_block.first); } } catch(InvalidPositionException &e){ succeeded = false; } - dispatchEvent(&event); + dispatchEvent(event); return succeeded; } @@ -350,79 +307,19 @@ bool Map::removeNodeWithEvent(v3s16 p) removeNodeAndUpdate(p, modified_blocks); // Copy modified_blocks to event - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - event.modified_blocks.insert(i->first); + for (auto &modified_block : modified_blocks) { + event.modified_blocks.insert(modified_block.first); } } catch(InvalidPositionException &e){ succeeded = false; } - dispatchEvent(&event); + dispatchEvent(event); return succeeded; } -bool Map::getDayNightDiff(v3s16 blockpos) -{ - try{ - v3s16 p = blockpos + v3s16(0,0,0); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - // Leading edges - try{ - v3s16 p = blockpos + v3s16(-1,0,0); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - try{ - v3s16 p = blockpos + v3s16(0,-1,0); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - try{ - v3s16 p = blockpos + v3s16(0,0,-1); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - // Trailing edges - try{ - v3s16 p = blockpos + v3s16(1,0,0); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - try{ - v3s16 p = blockpos + v3s16(0,1,0); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - try{ - v3s16 p = blockpos + v3s16(0,0,1); - MapBlock *b = getBlockNoCreate(p); - if(b->getDayNightDiff()) - return true; - } - catch(InvalidPositionException &e){} - - return false; -} - struct TimeOrderedMapBlock { MapSector *sect; MapBlock *block; @@ -444,7 +341,7 @@ struct TimeOrderedMapBlock { void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, std::vector *unloaded_blocks) { - bool save_before_unloading = (mapType() == MAPTYPE_SERVER); + bool save_before_unloading = maySaveBlocks(); // Profile modified reasons Profiler modprofiler; @@ -454,23 +351,20 @@ void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, u32 saved_blocks_count = 0; u32 block_count_all = 0; + const auto start_time = porting::getTimeUs(); beginSave(); // 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; + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; bool all_blocks_deleted = true; MapBlockVect blocks; sector->getBlocks(blocks); - for (MapBlockVect::iterator i = blocks.begin(); - i != blocks.end(); ++i) { - MapBlock *block = (*i); - + for (MapBlock *block : blocks) { block->incrementUsageTimer(dtime); if (block->refGet() == 0 @@ -499,28 +393,26 @@ void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, } } + // Delete sector if we emptied it if (all_blocks_deleted) { - sector_deletion_queue.push_back(si->first); + sector_deletion_queue.push_back(sector_it.first); } } } else { std::priority_queue mapblock_queue; - for (std::map::iterator si = m_sectors.begin(); - si != m_sectors.end(); ++si) { - MapSector *sector = si->second; + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; MapBlockVect blocks; sector->getBlocks(blocks); - for(MapBlockVect::iterator i = blocks.begin(); - i != blocks.end(); ++i) { - MapBlock *block = (*i); - + for (MapBlock *block : blocks) { 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)) { @@ -551,15 +443,19 @@ void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, 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); + for (auto §or_it : m_sectors) { + if (sector_it.second->empty()) { + sector_deletion_queue.push_back(sector_it.first); } } } + endSave(); + const auto end_time = porting::getTimeUs(); + + reportMetrics(end_time - start_time, saved_blocks_count, block_count_all); // Finally delete the empty sectors deleteSectors(sector_deletion_queue); @@ -588,14 +484,13 @@ void Map::unloadUnreferencedBlocks(std::vector *unloaded_blocks) void Map::deleteSectors(std::vector §orList) { - for(std::vector::iterator j = sectorList.begin(); - j != sectorList.end(); ++j) { - MapSector *sector = m_sectors[*j]; + for (v2s16 j : sectorList) { + MapSector *sector = m_sectors[j]; // If sector is in sector cache, remove it from there if(m_sector_cache == sector) m_sector_cache = NULL; // Remove from map and delete - m_sectors.erase(*j); + m_sectors.erase(j); delete sector; } } @@ -607,41 +502,45 @@ void Map::PrintInfo(std::ostream &out) #define WATER_DROP_BOOST 4 -enum NeighborType { +const static v3s16 liquid_6dirs[6] = { + // order: upper before same level before lower + v3s16( 0, 1, 0), + v3s16( 0, 0, 1), + v3s16( 1, 0, 0), + v3s16( 0, 0,-1), + v3s16(-1, 0, 0), + v3s16( 0,-1, 0) +}; + +enum NeighborType : u8 { NEIGHBOR_UPPER, NEIGHBOR_SAME_LEVEL, NEIGHBOR_LOWER }; + struct NodeNeighbor { MapNode n; NeighborType t; v3s16 p; - bool l; //can liquid NodeNeighbor() - : n(CONTENT_AIR) + : n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL) { } - NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos) + NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos) : n(node), t(n_type), p(pos) { } }; -void Map::transforming_liquid_add(v3s16 p) { +void ServerMap::transforming_liquid_add(v3s16 p) { m_transforming_liquid.push_back(p); } -s32 Map::transforming_liquid_size() { - return m_transforming_liquid.size(); -} - -void Map::transformLiquids(std::map &modified_blocks) +void ServerMap::transformLiquids(std::map &modified_blocks, + ServerEnvironment *env) { - DSTACK(FUNCTION_NAME); - //TimeTaker timer("transformLiquids()"); - u32 loopcount = 0; u32 initial_size = m_transforming_liquid.size(); @@ -656,23 +555,6 @@ void Map::transformLiquids(std::map &modified_blocks) u32 liquid_loop_max = g_settings->getS32("liquid_loop_max"); u32 loop_max = 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; - } - - 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 @@ -686,7 +568,7 @@ void Map::transformLiquids(std::map &modified_blocks) v3s16 p0 = m_transforming_liquid.front(); m_transforming_liquid.pop_front(); - MapNode n0 = getNodeNoEx(p0); + MapNode n0 = getNode(p0); /* Collect information about current node @@ -703,7 +585,7 @@ void Map::transformLiquids(std::map &modified_blocks) switch (liquid_type) { case LIQUID_SOURCE: liquid_level = LIQUID_LEVEL_SOURCE; - liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing); + liquid_kind = cf.liquid_alternative_flowing_id; break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); @@ -722,7 +604,6 @@ void Map::transformLiquids(std::map &modified_blocks) /* Collect information about the environment */ - const v3s16 *dirs = g_6dirs; NodeNeighbor sources[6]; // surrounding sources int num_sources = 0; NodeNeighbor flows[6]; // surrounding flowing liquid nodes @@ -736,15 +617,17 @@ void Map::transformLiquids(std::map &modified_blocks) for (u16 i = 0; i < 6; i++) { NeighborType nt = NEIGHBOR_SAME_LEVEL; switch (i) { - case 1: + case 0: nt = NEIGHBOR_UPPER; break; - case 4: + case 5: nt = NEIGHBOR_LOWER; break; + default: + break; } - v3s16 npos = p0 + dirs[i]; - NodeNeighbor nb(getNodeNoEx(npos), nt, npos); + v3s16 npos = p0 + liquid_6dirs[i]; + NodeNeighbor nb(getNode(npos), nt, npos); const ContentFeatures &cfnb = m_nodedef->get(nb.n); switch (m_nodedef->get(nb.n.getContent()).liquid_type) { case LIQUID_NONE: @@ -774,20 +657,24 @@ void Map::transformLiquids(std::map &modified_blocks) 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 = m_nodedef->getId(cfnb.liquid_alternative_flowing); - if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = cfnb.liquid_alternative_flowing_id; + if (cfnb.liquid_alternative_flowing_id != liquid_kind) { neutrals[num_neutrals++] = nb; } else { // Do not count bottom source, it will screw things up - if(dirs[i].Y != -1) + if(nt != NEIGHBOR_LOWER) 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 = m_nodedef->getId(cfnb.liquid_alternative_flowing); - if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + if (nb.t != NEIGHBOR_SAME_LEVEL || + (nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) { + // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + // but exclude falling liquids on the same level, they cannot flow here anyway + if (liquid_kind == CONTENT_AIR) + liquid_kind = cfnb.liquid_alternative_flowing_id; + } + if (cfnb.liquid_alternative_flowing_id != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -813,7 +700,7 @@ void Map::transformLiquids(std::map &modified_blocks) // 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 = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source); + new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id; } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) { // liquid_kind is set properly, see above max_node_level = new_node_level = LIQUID_LEVEL_MAX; @@ -894,11 +781,19 @@ void Map::transformLiquids(std::map &modified_blocks) // 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); + // set the liquid level and flow bits 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); @@ -929,7 +824,7 @@ void Map::transformLiquids(std::map &modified_blocks) MapBlock *block = getBlockNoCreateNoEx(blockpos); if (block != NULL) { modified_blocks[blockpos] = block; - changed_nodes.push_back(std::pair(p0, n00)); + changed_nodes.emplace_back(p0, n00); } /* @@ -955,11 +850,11 @@ void Map::transformLiquids(std::map &modified_blocks) } //infostream<<"Map::transformLiquids(): loopcount="<::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) - m_transforming_liquid.push_back(*iter); + for (auto &iter : must_reflow) + m_transforming_liquid.push_back(iter); voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks); - + env->getScriptIface()->on_liquid_transformed(changed_nodes); /* ---------------------------------------------------------------------- * Manage the queue so that it does not grow indefinately @@ -971,7 +866,7 @@ void Map::transformLiquids(std::map &modified_blocks) time_until_purge *= 1000; // seconds -> milliseconds - u32 curr_time = getTime(PRECISION_MILLI); + u64 curr_time = porting::getTimeMs(); u32 prev_unprocessed = m_unprocessed_count; m_unprocessed_count = m_transforming_liquid.size(); @@ -1157,24 +1052,95 @@ void Map::removeNodeTimer(v3s16 p) block->m_node_timers.remove(p_rel); } -bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, - float start_off, float end_off, u32 needed_count) +bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera, + const core::aabbox3d &block_bounds, v3s16 &check) +{ + /* + This functions determines the node inside the target block that is + closest to the camera position. This increases the occlusion culling + accuracy in straight and diagonal corridors. + The returned position will be occlusion checked first in addition to the + others (8 corners + center). + No position is returned if + - the closest node is a corner, corners are checked anyway. + - the camera is inside the target block, it will never be occluded. + */ +#define CLOSEST_EDGE(pos, bounds, axis) \ + ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \ + (bounds).MaxEdge.axis + + bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) && + (pos_camera.X <= block_bounds.MaxEdge.X); + bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) && + (pos_camera.Y <= block_bounds.MaxEdge.Y); + bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) && + (pos_camera.Z <= block_bounds.MaxEdge.Z); + + if (x_inside && y_inside && z_inside) + return false; // Camera inside target mapblock + + // straight + if (x_inside && y_inside) { + check = v3s16(pos_camera.X, pos_camera.Y, 0); + check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z); + return true; + } else if (y_inside && z_inside) { + check = v3s16(0, pos_camera.Y, pos_camera.Z); + check.X = CLOSEST_EDGE(pos_camera, block_bounds, X); + return true; + } else if (x_inside && z_inside) { + check = v3s16(pos_camera.X, 0, pos_camera.Z); + check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y); + return true; + } + + // diagonal + if (x_inside) { + check = v3s16(pos_camera.X, 0, 0); + check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y); + check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z); + return true; + } else if (y_inside) { + check = v3s16(0, pos_camera.Y, 0); + check.X = CLOSEST_EDGE(pos_camera, block_bounds, X); + check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z); + return true; + } else if (z_inside) { + check = v3s16(0, 0, pos_camera.Z); + check.X = CLOSEST_EDGE(pos_camera, block_bounds, X); + check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y); + return true; + } + + // Closest node would be a corner, none returned + return false; +} + +bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target, + float step, float stepfac, float offset, float end_offset, u32 needed_count) { - 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; + v3f direction = intToFloat(pos_target - pos_camera, BS); + float distance = direction.getLength(); + + // Normalize direction vector + if (distance > 0.0f) + direction /= distance; + + v3f pos_origin_f = intToFloat(pos_camera, BS); u32 count = 0; - for(float s=start_off; sget(n); - if(f.drawtype == NDT_NORMAL){ - // not transparent, see ContentFeature::updateTextures + bool is_valid_position; + + for (; offset < distance + end_offset; offset += step) { + v3f pos_node_f = pos_origin_f + direction * offset; + v3s16 pos_node = floatToInt(pos_node_f, BS); + + MapNode node = getNode(pos_node, &is_valid_position); + + if (is_valid_position && + !m_nodedef->get(node).light_propagates) { + // Cannot see through light-blocking nodes --> occluded count++; - if(count >= needed_count) + if (count >= needed_count) return true; } step *= stepfac; @@ -1182,55 +1148,69 @@ bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, return false; } -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; +bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) +{ + // Check occlusion for center and all 8 corners of the mapblock + // Overshoot a little for less flickering + static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1; + static const v3s16 dir9[9] = { + v3s16( 0, 0, 0), + v3s16( 1, 1, 1) * bs2, + v3s16( 1, 1, -1) * bs2, + v3s16( 1, -1, 1) * bs2, + v3s16( 1, -1, -1) * bs2, + v3s16(-1, 1, 1) * bs2, + v3s16(-1, 1, -1) * bs2, + v3s16(-1, -1, 1) * bs2, + v3s16(-1, -1, -1) * bs2, + }; + + v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2); + + // Starting step size, value between 1m and sqrt(3)m + float step = BS * 1.2f; + // Multiply step by each iteraction by 'stepfac' to reduce checks in distance + float stepfac = 1.05f; + + float start_offset = BS * 1.0f; + // 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. + // point by distance 'end_offset' to not enter the target mapblock. + // For the 8 mapblock corners 'end_offset' 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; + float end_offset = -BS * MAP_BLOCKSIZE * 1.732f; + // 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)); + // Additional occlusion check, see comments in that function + v3s16 check; + if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) { + // node is always on a side facing the camera, end_offset can be lower + if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset, + -1.0f, needed_count)) + return false; + } + + for (const v3s16 &dir : dir9) { + if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac, + start_offset, end_offset, needed_count)) + return false; + } + return true; } /* ServerMap */ -ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge): - Map(dout_server, gamedef), - settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), - m_emerge(emerge), - m_map_metadata_changed(true) +ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, + EmergeManager *emerge, MetricsBackend *mb): + Map(gamedef), + settings_mgr(savedir + DIR_DELIM + "map_meta.txt"), + m_emerge(emerge) { verbosestream<addCounter( + "minetest_map_save_time", "Time spent saving blocks (in microseconds)"); + m_save_count_counter = mb->addCounter( + "minetest_map_saved_blocks", "Number of blocks saved"); + m_loaded_blocks_gauge = mb->addGauge( + "minetest_map_loaded_blocks", "Number of loaded blocks"); + + m_map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9); + + try { // If directory exists, check contents and load if possible - if(fs::PathExists(m_savedir)) - { + if (fs::PathExists(m_savedir)) { // If directory is empty, it is safe to save into it. - if(fs::GetDirListing(m_savedir).size() == 0) - { + if (fs::GetDirListing(m_savedir).empty()) { infostream<<"ServerMap: Empty save directory is valid." <::Iterator i = m_chunks.getIterator(); - for(; i.atEnd() == false; i++) - { - MapChunk *chunk = i.getNode()->getValue(); - delete chunk; - } -#endif + delete dbase_ro; } MapgenParams *ServerMap::getMapgenParams() @@ -1362,9 +1329,17 @@ u64 ServerMap::getSeed() return getMapgenParams()->seed; } -s16 ServerMap::getWaterLevel() +bool ServerMap::blockpos_over_mapgen_limit(v3s16 p) { - return getMapgenParams()->water_level; + 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(v3s16 blockpos, BlockMakeData *data) @@ -1373,6 +1348,9 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize); v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1); + if (!m_chunks_in_progress.insert(bpmin).second) + return false; + bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax)); @@ -1380,15 +1358,14 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) v3s16 full_bpmin = bpmin - extra_borders; v3s16 full_bpmax = bpmax + extra_borders; - // Do nothing if not inside limits (+-1 because of neighbors) - if (blockpos_over_limit(full_bpmin) || - blockpos_over_limit(full_bpmax)) + // 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 = getSeed(); data->blockpos_min = bpmin; data->blockpos_max = bpmax; - data->blockpos_requested = blockpos; data->nodedef = m_nodedef; /* @@ -1398,7 +1375,7 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) 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); + MapSector *sector = createSector(sectorpos); FATAL_ERROR_IF(sector == NULL, "createSector() failed"); for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) { @@ -1426,25 +1403,6 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) data->vmanip = new MMVManip(this); data->vmanip->initialEmerge(full_bpmin, full_bpmax); - // 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; - n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z)); - if (n == NULL) - continue; - u8 flags = n->getValue(); - flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE; - n->setValue(flags); - } - } - } -#endif - // Data is ready now. return true; } @@ -1455,8 +1413,6 @@ void ServerMap::finishBlockMake(BlockMakeData *data, 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)); @@ -1477,10 +1433,8 @@ void ServerMap::finishBlockMake(BlockMakeData *data, data->transforming_liquid.pop_front(); } - for (std::map::iterator - it = changed_blocks->begin(); - it != changed_blocks->end(); ++it) { - MapBlock *block = it->second; + for (auto &changed_block : *changed_blocks) { + MapBlock *block = changed_block.second; if (!block) continue; /* @@ -1512,71 +1466,29 @@ void ServerMap::finishBlockMake(BlockMakeData *data, NOTE: Will be saved later. */ //save(MOD_STATE_WRITE_AT_UNLOAD); + m_chunks_in_progress.erase(bpmin); } -ServerMapSector *ServerMap::createSector(v2s16 p2d) +MapSector *ServerMap::createSector(v2s16 p2d) { - DSTACKF("%s: p2d=(%d,%d)", - FUNCTION_NAME, - p2d.X, p2d.Y); - /* Check if it exists already in memory */ - ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d); - if(sector != NULL) - return sector; - - /* - Try to load it from disk (with blocks) - */ - //if(loadSectorFull(p2d) == true) - - /* - Try to load metadata from disk - */ -#if 0 - if(loadSectorMeta(p2d) == true) - { - ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d); - if(sector == NULL) - { - infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"< mapgenlimit. - - Block minimum nodepos = blockpos * mapblocksize. - Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. + Do not create over max mapgen limit */ - const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")); - if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit - || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit - || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit) - throw InvalidPositionException("createSector(): pos. over limit"); + if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y))) + throw InvalidPositionException("createSector(): pos. over max mapgen limit"); /* Generate blank sector */ - sector = new ServerMapSector(this, p2d, m_gamedef); - - // Sector position on map in nodes - //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; + sector = new MapSector(this, p2d, m_gamedef); /* Insert to container @@ -1586,191 +1498,53 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) return sector; } -#if 0 -/* - This is a quick-hand function for calling makeBlock(). -*/ -MapBlock * ServerMap::generateBlock( - v3s16 p, - std::map &modified_blocks -) +MapBlock * ServerMap::createBlock(v3s16 p) { - DSTACKF("%s: p=(%d,%d,%d)", FUNCTION_NAME, p.X, p.Y, p.Z); - - /*infostream<<"generateBlock(): " - <<"("<getBool("enable_mapgen_debug_info"); - - TimeTaker timer("generateBlock"); - - //MapBlock *block = original_dummy; - - v2s16 p2d(p.X, p.Z); - v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE; - /* - Do not generate over-limit + Do not create over max mapgen limit */ - if(blockpos_over_limit(p)) - { - infostream<makeChunk(&data); - //mapgen::make_block(&data); - - if(enable_mapgen_debug_info == false) - t.stop(true); // Hide output + MapSector *sector; + try { + sector = createSector(p2d); + } catch (InvalidPositionException &e) { + infostream<<"createBlock: createSector() failed"<getBlockNoCreateNoEx(block_y); + if (block) { + if(block->isDummy()) + block->unDummify(); + return block; + } + // Create blank + block = sector->createBlankBlock(block_y); -#if 0 - /* - Check result - */ - if(block) + return block; +} + +MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) +{ { - bool erroneus_content = false; - for(s16 z0=0; z0getNode(p); - if(n.getContent() == CONTENT_IGNORE) - { - infostream<<"CONTENT_IGNORE at " - <<"("<setNode(v3s16(x0,y0,z0), n); - } - } - } -#endif - - if(enable_mapgen_debug_info == false) - timer.stop(true); // Hide output - - return block; -} -#endif - -MapBlock * ServerMap::createBlock(v3s16 p) -{ - DSTACKF("%s: p=(%d,%d,%d)", - FUNCTION_NAME, p.X, p.Y, p.Z); - - /* - Do not create over-limit - */ - if (blockpos_over_limit(p)) - throw InvalidPositionException("createBlock(): 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. - - NOTE: On old save formats, this will be slow, as it generates - lighting on blocks for them. - */ - ServerMapSector *sector; - try { - sector = (ServerMapSector*)createSector(p2d); - assert(sector->getId() == MAPSECTOR_SERVER); - } - catch(InvalidPositionException &e) - { - infostream<<"createBlock: createSector() failed"<getBlockNoCreateNoEx(block_y); - if(block) - { - if(block->isDummy()) - block->unDummify(); - return block; - } - // Create blank - block = sector->createBlankBlock(block_y); - - return block; -} - -MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) -{ - DSTACKF("%s: p=(%d,%d,%d), create_blank=%d", - FUNCTION_NAME, - p.X, p.Y, p.Z, create_blank); - - { - MapBlock *block = getBlockNoCreateNoEx(p); - if(block && block->isDummy() == false) - return block; + MapBlock *block = getBlockNoCreateNoEx(p); + if (block && !block->isDummy()) + return block; } { @@ -1780,39 +1554,12 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) } if (create_blank) { - ServerMapSector *sector = createSector(v2s16(p.X, p.Z)); + MapSector *sector = createSector(v2s16(p.X, p.Z)); MapBlock *block = sector->createBlankBlock(p.Y); return block; } -#if 0 - if(allow_generate) - { - std::map modified_blocks; - MapBlock *block = generateBlock(p, modified_blocks); - if(block) - { - MapEditEvent event; - event.type = MEET_OTHER; - event.p = p; - - // Copy modified_blocks to event - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - event.modified_blocks.insert(i->first); - } - - // Queue event - dispatchEvent(&event); - - return block; - } - } -#endif - return NULL; } @@ -1825,7 +1572,32 @@ MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d) return block; } -void ServerMap::prepareBlock(MapBlock *block) { +bool ServerMap::isBlockInQueue(v3s16 pos) +{ + return m_emerge && m_emerge->isBlockInQueue(pos); +} + +void ServerMap::addNodeAndUpdate(v3s16 p, MapNode n, + std::map &modified_blocks, + bool remove_metadata) +{ + Map::addNodeAndUpdate(p, n, modified_blocks, remove_metadata); + + /* + Add neighboring liquid nodes and this node to transform queue. + (it's vital for the node itself to get updated last, if it was removed.) + */ + + for (const v3s16 &dir : g_7dirs) { + v3s16 p2 = p + dir; + + bool is_valid_position; + MapNode n2 = getNode(p2, &is_valid_position); + if(is_valid_position && + (m_nodedef->get(n2).isLiquid() || + n2.getContent() == CONTENT_AIR)) + m_transforming_liquid.push_back(p2); + } } // N.B. This requires no synchronization, since data will not be modified unless @@ -1844,155 +1616,28 @@ void ServerMap::updateVManip(v3s16 pos) return; s32 idx = vm->m_area.index(pos); - vm->m_data[idx] = getNodeNoEx(pos); + vm->m_data[idx] = getNode(pos); vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA; vm->m_is_dirty = true; } -s16 ServerMap::findGroundLevel(v2s16 p2d) -{ -#if 0 - /* - Uh, just do something random... - */ - // Find existing map from top to down - s16 max=63; - s16 min=-64; - v3s16 p(p2d.X, max, p2d.Y); - for(; p.Y>min; p.Y--) - { - MapNode n = getNodeNoEx(p); - if(n.getContent() != CONTENT_IGNORE) - break; - } - if(p.Y == min) - goto plan_b; - // If this node is not air, go to plan b - if(getNodeNoEx(p).getContent() != CONTENT_AIR) - goto plan_b; - // Search existing walkable and return it - for(; p.Y>min; p.Y--) - { - MapNode n = getNodeNoEx(p); - if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE) - return p.Y; - } - - // Move to plan b -plan_b: -#endif - - /* - Determine from map generator noise functions - */ - - s16 level = m_emerge->getGroundLevelAtPoint(p2d); - return level; - - //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT; - //return (s16)level; -} - -bool ServerMap::loadFromFolders() { - if (!dbase->initialized() && - !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) - return true; - return false; -} - -void ServerMap::createDirs(std::string path) -{ - if(fs::CreateAllDirs(path) == false) - { - m_dout<<"ServerMap: Failed to create directory " - <<"\""<set(all_blocks); + m_save_time_counter->increment(save_time_us); + m_save_count_counter->increment(saved_blocks); } void ServerMap::save(ModifiedState save_level) { - DSTACK(FUNCTION_NAME); - if(m_map_saving_enabled == false) { + if (!m_map_saving_enabled) { warningstream<<"Not saving map, saving disabled."<::iterator i = m_sectors.begin(); - 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) { - saveSectorMeta(sector); - sector_meta_count++; - } + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; MapBlockVect blocks; sector->getBlocks(blocks); - for(MapBlockVect::iterator j = blocks.begin(); - j != blocks.end(); ++j) { - MapBlock *block = *j; - + for (MapBlock *block : blocks) { block_count_all++; if(block->getModified() >= (u32)save_level) { @@ -2042,12 +1676,6 @@ void ServerMap::save(ModifiedState save_level) saveBlock(block); block_count++; - - /*infostream<<"ServerMap: Written block (" - <getPos().X<<"," - <getPos().Y<<"," - <getPos().Z<<")" - < &dst) { - if (loadFromFolders()) { - errorstream << "Map::listAllLoadableBlocks(): Result will be missing " - << "all blocks that are stored in flat files." << std::endl; - } dbase->listAllLoadableBlocks(dst); + if (dbase_ro) + dbase_ro->listAllLoadableBlocks(dst); } -void ServerMap::listAllLoadedBlocks(std::vector &dst) -{ - for(std::map::iterator si = m_sectors.begin(); - si != m_sectors.end(); ++si) - { - MapSector *sector = si->second; - - MapBlockVect blocks; - sector->getBlocks(blocks); - - for(MapBlockVect::iterator i = blocks.begin(); - i != blocks.end(); ++i) { - v3s16 p = (*i)->getPos(); - dst.push_back(p); - } - } -} - -void ServerMap::saveSectorMeta(ServerMapSector *sector) -{ - DSTACK(FUNCTION_NAME); - // Format used for writing - u8 version = SER_FMT_VER_HIGHEST_WRITE; - // Get destination - v2s16 pos = sector->getPos(); - std::string dir = getSectorDir(pos); - createDirs(dir); - - std::string fullpath = dir + DIR_DELIM + "meta"; - std::ostringstream ss(std::ios_base::binary); - - sector->serialize(ss, version); - - if(!fs::safeWriteToFile(fullpath, ss.str())) - throw FileNotGoodException("Cannot write sector metafile"); - - sector->differs_from_disk = false; -} - -MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load) -{ - DSTACK(FUNCTION_NAME); - // Get destination - v2s16 p2d = getSectorPos(sectordir); - - ServerMapSector *sector = NULL; - - std::string fullpath = sectordir + DIR_DELIM + "meta"; - std::ifstream is(fullpath.c_str(), std::ios_base::binary); - if(is.good() == false) - { - // If the directory exists anyway, it probably is in some old - // format. Just go ahead and create the sector. - if(fs::PathExists(sectordir)) - { - /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile " - <differs_from_disk = false; - - return sector; -} - -bool ServerMap::loadSectorMeta(v2s16 p2d) -{ - DSTACK(FUNCTION_NAME); - - // 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); - } - - try{ - loadSectorMeta(sectordir, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - return false; - } - catch(FileNotGoodException &e) - { - return false; - } - catch(std::exception &e) - { - return false; - } - - return true; -} - -#if 0 -bool ServerMap::loadSectorFull(v2s16 p2d) -{ - DSTACK(FUNCTION_NAME); - - MapSector *sector = NULL; - - // 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); - } - - try{ - sector = loadSectorMeta(sectordir, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - return false; - } - catch(FileNotGoodException &e) - { - return false; - } - catch(std::exception &e) - { - return false; - } - - /* - Load blocks - */ - std::vector list2 = fs::GetDirListing - (sectordir); - std::vector::iterator i2; - for(i2=list2.begin(); i2!=list2.end(); i2++) - { - // We want files - if(i2->dir) - continue; - try{ - loadBlock(sectordir, i2->name, sector, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - // This catches unknown crap in directory - } - } - - if(loadlayout != 2) - { - infostream<<"Sector converted to new layout - deleting "<< - sectordir1<getPos(); @@ -2333,10 +1771,9 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db) */ std::ostringstream o(std::ios_base::binary); o.write((char*) &version, 1); - block->serialize(o, version, true); + block->serialize(o, version, true, compression_level); - std::string data = o.str(); - bool ret = db->saveBlock(p3d, data); + bool ret = db->saveBlock(p3d, o.str()); if (ret) { // We just wrote it to the disk so clear modified flag block->resetModified(); @@ -2344,90 +1781,8 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db) return ret; } -void ServerMap::loadBlock(std::string sectordir, 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) - throw FileNotGoodException("Cannot open block file"); - - v3s16 p3d = getBlockPos(sectordir, blockfile); - v2s16 p2d(p3d.X, p3d.Z); - - assert(sector->getPos() == p2d); - - 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 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); - ReflowScan scanner(this, m_emerge->ndef); - scanner.scan(block, &m_transforming_liquid); - } - - /* - Save blocks loaded in old format in new format - */ - - if(version < SER_FMT_VER_HIGHEST_WRITE || 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. - block->resetModified(); - - } - catch(SerializationError &e) - { - warningstream<<"Invalid block data on disk " - <<"fullpath="<loadBlock(blockpos, &ret); + if (!ret.empty()) { + loadBlock(&ret, blockpos, createSector(p2d), false); } - - /* - 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; - } - } - - - /* - Make sure file exists - */ - - 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); + } else { + return NULL; } + MapBlock *block = getBlockNoCreateNoEx(blockpos); if (created_new && (block != NULL)) { std::map modified_blocks; @@ -2561,7 +1874,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos) for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it) event.modified_blocks.insert(it->first); - dispatchEvent(&event); + dispatchEvent(event); } } return block; @@ -2575,7 +1888,7 @@ bool ServerMap::deleteBlock(v3s16 blockpos) MapBlock *block = getBlockNoCreateNoEx(blockpos); if (block) { v2s16 p2d(blockpos.X, blockpos.Z); - MapSector *sector = getSectorNoGenerateNoEx(p2d); + MapSector *sector = getSectorNoGenerate(p2d); if (!sector) return false; sector->deleteBlock(block); @@ -2589,16 +1902,21 @@ void ServerMap::PrintInfo(std::ostream &out) out<<"ServerMap: "; } -MMVManip::MMVManip(Map *map): - VoxelManipulator(), - m_is_dirty(false), - m_create_area(false), - m_map(map) +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; } -MMVManip::~MMVManip() +MMVManip::MMVManip(Map *map): + VoxelManipulator(), + m_map(map) { + assert(map); } void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, @@ -2606,6 +1924,8 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, { TimeTaker timer1("initialEmerge", &emerge_time); + assert(m_map); + // Units of these are MapBlocks v3s16 p_min = blockpos_min; v3s16 p_max = blockpos_max; @@ -2637,25 +1957,20 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, continue; bool block_data_inexistent = false; - try { - TimeTaker timer1("emerge load", &emerge_load_time); + TimeTaker timer2("emerge load", &emerge_load_time); - block = m_map->getBlockNoCreate(p); - if(block->isDummy()) + block = m_map->getBlockNoCreateNoEx(p); + if (!block || block->isDummy()) block_data_inexistent = true; else block->copyTo(*this); } - catch(InvalidPositionException &e) - { - block_data_inexistent = true; - } if(block_data_inexistent) { - if (load_if_inexistent && !blockpos_over_limit(p)) { + if (load_if_inexistent && !blockpos_over_max_limit(p)) { ServerMap *svrmap = (ServerMap *)m_map; block = svrmap->emergeBlock(p, false); if (block == NULL) @@ -2694,26 +2009,54 @@ void MMVManip::blitBackAll(std::map *modified_blocks, { if(m_area.getExtent() == v3s16(0,0,0)) return; + assert(m_map); /* Copy data of all blocks */ - for(std::map::iterator - i = m_loaded_blocks.begin(); - i != m_loaded_blocks.end(); ++i) - { - v3s16 p = i->first; + for (auto &loaded_block : m_loaded_blocks) { + v3s16 p = loaded_block.first; MapBlock *block = m_map->getBlockNoCreateNoEx(p); - bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST); - if ((existed == false) || (block == NULL) || - (overwrite_generated == false && block->isGenerated() == true)) + bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST); + if (!existed || (block == NULL) || + (!overwrite_generated && block->isGenerated())) continue; block->copyFrom(*this); + block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP); if(modified_blocks) (*modified_blocks)[p] = block; } } +MMVManip *MMVManip::clone() const +{ + MMVManip *ret = new MMVManip(); + + const s32 size = m_area.getVolume(); + ret->m_area = m_area; + if (m_data) { + ret->m_data = new MapNode[size]; + memcpy(ret->m_data, m_data, size * sizeof(MapNode)); + } + if (m_flags) { + ret->m_flags = new u8[size]; + memcpy(ret->m_flags, m_flags, size * sizeof(u8)); + } + + ret->m_is_dirty = m_is_dirty; + // Even if the copy is disconnected from a map object keep the information + // needed to write it back to one + ret->m_loaded_blocks = m_loaded_blocks; + + return ret; +} + +void MMVManip::reparent(Map *map) +{ + assert(map && !m_map); + m_map = map; +} + //END