s16 Map::getHumidity(v3s16 p, bool no_random) { MapBlock *block = getBlockNoCreateNoEx(getNodeBlockPos(p)); if(block != NULL) { s16 value = block->humidity; return value + (no_random ? 0 : myrand_range(0, 1)); } //errorstream << "No humidity for " << p.X<<"," << p.Z << std::endl; return 0; }
MapNode Map::getNodeTry(v3POS p) { #ifndef NDEBUG ScopeProfiler sp(g_profiler, "Map: getNodeTry"); #endif auto blockpos = getNodeBlockPos(p); auto block = getBlockNoCreateNoEx(blockpos, true); if(!block) return MapNode(CONTENT_IGNORE); auto relpos = p - blockpos * MAP_BLOCKSIZE; return block->getNodeTry(relpos); }
void Map::insertBlock(MapBlock *block) { auto block_p = block->getPos(); auto block2 = getBlockNoCreateNoEx(block_p, false, true); if(block2) { //throw AlreadyExistsException("Block already exists"); infostream << "Block already exists " << block_p << std::endl; return; // memory leak, but very rare|impossible } // Insert into container m_blocks.set(block_p, block); }
MapBlock * Map::createBlankBlock(v3POS & p) { auto lock = m_blocks.lock_unique_rec(); MapBlock *block = getBlockNoCreateNoEx(p, false, true); if (block != NULL) { infostream << "Block already created p=" << block->getPos() << std::endl; return block; } block = createBlankBlockNoInsert(p); m_blocks.set(p, block); return block; }
bool Map::insertBlock(MapBlock *block) { auto block_p = block->getPos(); auto lock = m_blocks.lock_unique_rec(); auto block2 = getBlockNoCreateNoEx(block_p, false, true); if(block2) { //throw AlreadyExistsException("Block already exists"); infostream << "Block already exists " << block_p << std::endl; return false; } // Insert into container m_blocks.set(block_p, block); return true; }
s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3POS p, MapBlock *block, std::map<v3POS, s16> * cache) { auto bp = getNodeBlockPos(p); auto gametime = env->getGameTime(); if (block) { if (gametime < block->humidity_last_update) return block->humidity + myrand_range(0, 1); } else if (!cache) { block = getBlockNoCreateNoEx(bp, true); } if (cache && cache->count(bp)) return cache->at(bp) + myrand_range(0, 1); auto value = m_emerge->biomemgr->calcBlockHumidity(p, getSeed(), env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed(), env->m_use_weather); if(block) { block->humidity = value; block->humidity_last_update = env->m_use_weather ? gametime + 30 : -1; } if (cache) (*cache)[bp] = value; return value + myrand_range(0, 1); }
MapBlockP Map::getBlock(v3POS p, bool trylock, bool nocache) { return getBlockNoCreateNoEx(p, trylock, nocache); }
u32 Map::transformLiquidsReal(Server *m_server, unsigned int max_cycle_ms) { INodeDefManager *nodemgr = m_gamedef->ndef(); DSTACK(__FUNCTION_NAME); //TimeTaker timer("transformLiquidsReal()"); u32 loopcount = 0; u32 initial_size = transforming_liquid_size(); s32 regenerated = 0; #if LIQUID_DEBUG bool debug = 1; #endif u8 relax = g_settings->getS16("liquid_relax"); bool fast_flood = g_settings->getS16("liquid_fast_flood"); int water_level = g_settings->getS16("water_level"); s16 liquid_pressure = m_server->m_emerge->params.liquid_pressure; //g_settings->getS16NoEx("liquid_pressure", liquid_pressure); // list of nodes that due to viscosity have not reached their max level height //std::unordered_map<v3POS, bool, v3POSHash, v3POSEqual> must_reflow, must_reflow_second, must_reflow_third; std::list<v3POS> must_reflow, must_reflow_second, must_reflow_third; // List of MapBlocks that will require a lighting update (due to lava) u16 loop_rand = myrand(); u32 end_ms = porting::getTimeMs() + max_cycle_ms; NEXT_LIQUID: ; while (transforming_liquid_size() > 0) { // This should be done here so that it is done when continue is used if (loopcount >= initial_size * 2 || porting::getTimeMs() > end_ms) break; ++loopcount; /* Get a queued transforming liquid node */ v3POS p0; { //JMutexAutoLock lock(m_transforming_liquid_mutex); p0 = transforming_liquid_pop(); } s16 total_level = 0; //u16 level_max = 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; s8 can_liquid = 0; // warning! when MINETEST_PROTO enabled - CONTENT_IGNORE != 0 content_t liquid_kind = CONTENT_IGNORE; content_t liquid_kind_flowing = CONTENT_IGNORE; content_t melt_kind = CONTENT_IGNORE; content_t melt_kind_flowing = CONTENT_IGNORE; //s8 viscosity = 0; /* Collect information about the environment, start from self */ for (u8 e = 0; e < 7; e++) { u8 i = liquid_explore_map[e]; NodeNeighbor & nb = neighbors[i]; nb.pos = p0 + liquid_flow_dirs[i]; nb.node = getNodeNoEx(neighbors[i].pos); nb.content = nb.node.getContent(); NeighborType nt = NEIGHBOR_SAME_LEVEL; switch (i) { case D_TOP: nt = NEIGHBOR_UPPER; break; case D_BOTTOM: nt = NEIGHBOR_LOWER; break; } nb.type = nt; nb.liquid = 0; nb.infinity = 0; nb.weight = 0; nb.drop = 0; if (!nb.node) { //if (i == D_SELF && (loopcount % 2) && initial_size < m_liquid_step_flow * 3) // must_reflow_third[nb.pos] = 1; // must_reflow_third.push_back(nb.pos); continue; } auto f = nodemgr->get(nb.content); switch (f.liquid_type) { case LIQUID_NONE: if (nb.content == CONTENT_AIR) { liquid_levels[i] = 0; nb.liquid = 1; } //TODO: if (nb.content == CONTENT_AIR || nodemgr->get(nb.node).buildable_to && !nodemgr->get(nb.node).walkable) { // need lua drop api for drop torches else if ( melt_kind_flowing != CONTENT_IGNORE && nb.content == melt_kind_flowing && nb.type != NEIGHBOR_UPPER && !(loopcount % 2)) { u8 melt_max_level = nb.node.getMaxLevel(nodemgr); u8 my_max_level = MapNode(liquid_kind_flowing).getMaxLevel(nodemgr); liquid_levels[i] = (float)my_max_level / melt_max_level * nb.node.getLevel(nodemgr); if (liquid_levels[i]) nb.liquid = 1; } else if ( melt_kind != CONTENT_IGNORE && nb.content == melt_kind && nb.type != NEIGHBOR_UPPER && !(loopcount % 8)) { liquid_levels[i] = nodemgr->get(liquid_kind_flowing).getMaxLevel(); if (liquid_levels[i]) nb.liquid = 1; } else { int drop = ((ItemGroupList) f.groups)["drop_by_liquid"]; if (drop && !(loopcount % drop) ) { liquid_levels[i] = 0; nb.liquid = 1; nb.drop = 1; } } // todo: for erosion add something here.. 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( f.liquid_alternative_flowing); if (liquid_kind == CONTENT_IGNORE) liquid_kind = nb.content; if (liquid_kind_flowing == CONTENT_IGNORE) liquid_kind_flowing = liquid_kind; if (melt_kind == CONTENT_IGNORE) melt_kind = nodemgr->getId(f.melt); if (melt_kind_flowing == CONTENT_IGNORE) melt_kind_flowing = nodemgr->getId( nodemgr->get(nodemgr->getId(f.melt) ).liquid_alternative_flowing); if (melt_kind_flowing == CONTENT_IGNORE) melt_kind_flowing = melt_kind; if (nb.content == liquid_kind) { if (nb.node.param2 & LIQUID_STABLE_MASK) continue; liquid_levels[i] = nb.node.getLevel(nodemgr); //LIQUID_LEVEL_SOURCE; nb.liquid = 1; nb.infinity = (nb.node.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.content; if (liquid_kind == CONTENT_IGNORE) liquid_kind = nodemgr->getId( f.liquid_alternative_source); if (liquid_kind == CONTENT_IGNORE) liquid_kind = liquid_kind_flowing; if (melt_kind_flowing == CONTENT_IGNORE) melt_kind_flowing = nodemgr->getId(f.melt); if (melt_kind == CONTENT_IGNORE) melt_kind = nodemgr->getId(nodemgr->get(nodemgr->getId( f.melt)).liquid_alternative_source); if (melt_kind == CONTENT_IGNORE) melt_kind = melt_kind_flowing; if (nb.content == liquid_kind_flowing) { if (nb.node.param2 & LIQUID_STABLE_MASK) continue; liquid_levels[i] = nb.node.getLevel(nodemgr); nb.liquid = 1; nb.infinity = (nb.node.param2 & LIQUID_INFINITY_MASK); } break; } // only self, top, bottom swap if (f.liquid_type && e <= 2) { try { nb.weight = ((ItemGroupList) f.groups)["weight"]; if (e == 1 && neighbors[D_BOTTOM].weight && neighbors[D_SELF].weight > neighbors[D_BOTTOM].weight) { setNode(neighbors[D_SELF].pos, neighbors[D_BOTTOM].node); setNode(neighbors[D_BOTTOM].pos, neighbors[D_SELF].node); //must_reflow_second[neighbors[D_SELF].pos] = 1; //must_reflow_second[neighbors[D_BOTTOM].pos] = 1; must_reflow_second.push_back(neighbors[D_SELF].pos); must_reflow_second.push_back(neighbors[D_BOTTOM].pos); #if LIQUID_DEBUG infostream << "Liquid swap1" << neighbors[D_SELF].pos << nodemgr->get(neighbors[D_SELF].node).name << neighbors[D_SELF].node << " w=" << neighbors[D_SELF].weight << " VS " << neighbors[D_BOTTOM].pos << nodemgr->get(neighbors[D_BOTTOM].node).name << neighbors[D_BOTTOM].node << " w=" << neighbors[D_BOTTOM].weight << std::endl; #endif goto NEXT_LIQUID; } if (e == 2 && neighbors[D_SELF].weight && neighbors[D_TOP].weight > neighbors[D_SELF].weight) { setNode(neighbors[D_SELF].pos, neighbors[D_TOP].node); setNode(neighbors[D_TOP].pos, neighbors[D_SELF].node); //must_reflow_second[neighbors[D_SELF].pos] = 1; //must_reflow_second[neighbors[D_TOP].pos] = 1; must_reflow_second.push_back(neighbors[D_SELF].pos); must_reflow_second.push_back(neighbors[D_TOP].pos); #if LIQUID_DEBUG infostream << "Liquid swap2" << neighbors[D_TOP].pos << nodemgr->get(neighbors[D_TOP].node).name << neighbors[D_TOP].node << " w=" << neighbors[D_TOP].weight << " VS " << neighbors[D_SELF].pos << nodemgr->get(neighbors[D_SELF].node).name << neighbors[D_SELF].node << " w=" << neighbors[D_SELF].weight << std::endl; #endif goto NEXT_LIQUID; } } catch(InvalidPositionException &e) { verbosestream << "transformLiquidsReal: weight: setNode() failed:" << nb.pos << ":" << e.what() << std::endl; //goto NEXT_LIQUID; } } if (nb.liquid) { liquid_levels_want[i] = 0; ++can_liquid; if(nb.type == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level; } if (liquid_levels[i] > 0) total_level += liquid_levels[i]; #if LIQUID_DEBUG infostream << "get node i=" << (int)i << " " << PP(nb.pos) << " c=" << nb.content << " p0=" << (int)nb.node.param0 << " p1=" << (int)nb.node.param1 << " p2=" << (int)nb.node.param2 << " lt=" << f.liquid_type //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing << " l=" << nb.liquid << " inf=" << nb.infinity << " nlevel=" << (int)liquid_levels[i] << " totallevel=" << (int)total_level << " cansame=" << (int)can_liquid_same_level << " Lmax=" << (int)nodemgr->get(liquid_kind_flowing).getMaxLevel() << std::endl; #endif } if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].liquid || total_level <= 0) continue; s16 level_max = nodemgr->get(liquid_kind_flowing).getMaxLevel(); s16 level_max_compressed = nodemgr->get(liquid_kind_flowing).getMaxLevel(1); s16 pressure = liquid_pressure ? ((ItemGroupList) nodemgr->get(liquid_kind).groups)["pressure"] : 0; auto liquid_renewable = nodemgr->get(liquid_kind).liquid_renewable; #if LIQUID_DEBUG s16 total_was = total_level; //debug #endif //viscosity = nodemgr->get(liquid_kind).viscosity; s16 level_avg = total_level / can_liquid; if (!pressure && level_avg) { level_avg = level_max; } #if LIQUID_DEBUG if (debug) infostream << " go: " << nodemgr->get(liquid_kind).name << " total_level=" << (int)total_level //<<" total_was="<<(int)total_was << " level_max=" << (int)level_max << " level_max_compressed=" << (int)level_max_compressed << " level_avg=" << (int)level_avg << " pressure=" << (int)pressure << " can_liquid=" << (int)can_liquid << " can_liquid_same_level=" << (int)can_liquid_same_level << std::endl; ; #endif // fill bottom block if (neighbors[D_BOTTOM].liquid) { liquid_levels_want[D_BOTTOM] = level_avg > level_max ? level_avg : total_level > level_max ? level_max : total_level; total_level -= liquid_levels_want[D_BOTTOM]; //if (pressure && total_level && liquid_levels_want[D_BOTTOM] < level_max_compressed) { // ++liquid_levels_want[D_BOTTOM]; // --total_level; //} } //relax up u16 relax_want = level_max * can_liquid_same_level; if ( liquid_renewable && relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && level_max > 1 && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] >= level_max && total_level >= relax_want - (can_liquid_same_level - relax) && total_level < relax_want && can_liquid_same_level >= relax + 1) { regenerated += relax_want - total_level; #if LIQUID_DEBUG infostream << " relax_up: " << " total_level=" << (int)total_level << " to=> " << int(relax_want) << std::endl; #endif total_level = relax_want; } // prevent lakes in air above unloaded blocks if ( liquid_levels[D_TOP] == 0 && p0.Y > water_level && level_max > 1 && !neighbors[D_BOTTOM].node && !(loopcount % 3)) { --total_level; #if LIQUID_DEBUG infostream << " above unloaded fix: " << " total_level=" << (int)total_level << std::endl; #endif } // calculate self level 5 blocks u16 want_level = level_avg > level_max ? level_avg : total_level >= level_max * can_liquid_same_level ? level_max : total_level / can_liquid_same_level; total_level -= want_level * can_liquid_same_level; /* if (pressure && total_level > 0 && neighbors[D_BOTTOM].liquid) { // bottom pressure +1 ++liquid_levels_want[D_BOTTOM]; --total_level; #if LIQUID_DEBUG infostream << " bottom1 pressure+1: " << " bottom=" << (int)liquid_levels_want[D_BOTTOM] << " total_level=" << (int)total_level << std::endl; #endif } */ //relax down if ( liquid_renewable && relax && p0.Y >= water_level - 1 && p0.Y <= water_level + 1 && liquid_levels[D_TOP] == 0 && (total_level <= 1 || !(loopcount % 2)) && level_max > 1 && liquid_levels[D_BOTTOM] >= level_max && want_level <= 0 && total_level <= (can_liquid_same_level - relax) && can_liquid_same_level >= relax + 1) { #if LIQUID_DEBUG infostream << " relax_down: " << " total_level WAS=" << (int)total_level << " to => 0" << std::endl; #endif regenerated -= total_level; total_level = 0; } for (u16 ir = D_SELF; ir < D_TOP; ++ir) { // fill only same level u16 ii = liquid_random_map[(loopcount + loop_rand + 1) % 4][ir]; if (!neighbors[ii].liquid) continue; liquid_levels_want[ii] = want_level; //if (viscosity > 1 && (liquid_levels_want[ii]-liquid_levels[ii]>8-viscosity)) // randomly place rest of divide if (liquid_levels_want[ii] < level_max && total_level > 0) { if (level_max > LIQUID_LEVEL_SOURCE || loopcount % 3 || liquid_levels[ii] <= 0) { if (liquid_levels[ii] > liquid_levels_want[ii]) { ++liquid_levels_want[ii]; --total_level; } } else { ++liquid_levels_want[ii]; --total_level; } } } for (u16 ir = D_SELF; ir < D_TOP; ++ir) { if (total_level < 1) break; u16 ii = liquid_random_map[(loopcount + loop_rand + 2) % 4][ir]; if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < level_max) { ++liquid_levels_want[ii]; --total_level; } } // fill top block if can if (neighbors[D_TOP].liquid && total_level > 0) { //infostream<<"compressing to top was="<<liquid_levels_want[D_TOP]<<" add="<<total_level<<std::endl; //liquid_levels_want[D_TOP] = total_level>level_max_compressed?level_max_compressed:total_level; liquid_levels_want[D_TOP] = total_level > level_max ? level_max : total_level; total_level -= liquid_levels_want[D_TOP]; //if (liquid_levels_want[D_TOP] && total_level && pressure) { if (total_level > 0 && pressure) { /* if (total_level > 0 && neighbors[D_BOTTOM].liquid) { // bottom pressure +2 ++liquid_levels_want[D_BOTTOM]; --total_level; } */ //compressing self level while can //for (u16 ir = D_SELF; ir < D_TOP; ++ir) { for (u16 ir = D_BOTTOM; ir <= D_TOP; ++ir) { if (total_level < 1) break; u16 ii = liquid_random_map[(loopcount + loop_rand + 3) % 4][ir]; if (neighbors[ii].liquid && liquid_levels_want[ii] < level_max_compressed) { ++liquid_levels_want[ii]; --total_level; } } /* if (total_level > 0 && neighbors[D_BOTTOM].liquid) { // bottom pressure +2 ++liquid_levels_want[D_BOTTOM]; --total_level; #if LIQUID_DEBUG infostream << " bottom2 pressure+1: " << " bottom=" << (int)liquid_levels_want[D_BOTTOM] << " total_level=" << (int)total_level << std::endl; #endif } */ } } if (pressure) { if (neighbors[D_BOTTOM].liquid && liquid_levels_want[D_BOTTOM] < level_max_compressed && liquid_levels_want[D_TOP] > 0 ) { //if (liquid_levels_want[D_BOTTOM] <= liquid_levels_want[D_TOP]) { --liquid_levels_want[D_TOP]; ++liquid_levels_want[D_BOTTOM]; #if LIQUID_DEBUG infostream << " bottom1 pressure+: " << " bot=" << (int)liquid_levels_want[D_BOTTOM] << " slf=" << (int)liquid_levels_want[D_SELF] << " top=" << (int)liquid_levels_want[D_TOP] << " total_level=" << (int)total_level << std::endl; #endif //} } else if ( neighbors[D_BOTTOM].liquid && liquid_levels_want[D_BOTTOM] < level_max_compressed && liquid_levels_want[D_SELF] > level_max ) { if (liquid_levels_want[D_BOTTOM] <= liquid_levels_want[D_SELF]) { --liquid_levels_want[D_SELF]; ++liquid_levels_want[D_BOTTOM]; #if LIQUID_DEBUG infostream << " bottom2 pressure+: " << " bot=" << (int)liquid_levels_want[D_BOTTOM] << " slf=" << (int)liquid_levels_want[D_SELF] << " top=" << (int)liquid_levels_want[D_TOP] << " total_level=" << (int)total_level << std::endl; #endif } } else if ( neighbors[D_TOP].liquid && liquid_levels_want[D_SELF] < level_max_compressed && liquid_levels_want[D_TOP] > level_max ) { if (liquid_levels_want[D_SELF] <= liquid_levels_want[D_TOP]) { --liquid_levels_want[D_TOP]; ++liquid_levels_want[D_SELF]; #if LIQUID_DEBUG infostream << " bottom3 pressure+: " << " bot=" << (int)liquid_levels_want[D_BOTTOM] << " slf=" << (int)liquid_levels_want[D_SELF] << " top=" << (int)liquid_levels_want[D_TOP] << " total_level=" << (int)total_level << std::endl; #endif } } if (liquid_levels_want[D_TOP] > level_max && relax && total_level <= 0 && level_avg > level_max && liquid_levels_want[D_TOP] < level_avg) { #if LIQUID_DEBUG infostream << " top pressure relax: " << " top=" << (int)liquid_levels_want[D_TOP] << " to=>" << level_avg << std::endl; #endif //regenerated += level_avg - liquid_levels_want[D_TOP]; //liquid_levels_want[D_TOP] = level_avg; regenerated += 1 ; liquid_levels_want[D_TOP] += 1; } } #if LIQUID_DEBUG if (total_level > 0) infostream << " rest 1: " << " wtop=" << (int)liquid_levels_want[D_TOP] << " total_level=" << (int)total_level << std::endl; #endif if (total_level > 0 && neighbors[D_TOP].liquid && liquid_levels_want[D_TOP] < level_max_compressed) { s16 add = (total_level > level_max_compressed - liquid_levels_want[D_TOP]) ? level_max_compressed - liquid_levels_want[D_TOP] : total_level; liquid_levels_want[D_TOP] += add; total_level -= add; } if (total_level > 0 && neighbors[D_SELF].liquid && liquid_levels_want[D_SELF] < level_max_compressed) { // very rare, compressed only s16 add = (total_level > level_max_compressed - liquid_levels_want[D_SELF]) ? level_max_compressed - liquid_levels_want[D_SELF] : total_level; #if LIQUID_DEBUG if (total_level > 0) infostream << " rest 2: " << " wself=" << (int)liquid_levels_want[D_SELF] << " total_level=" << (int)total_level << " add=" << (int)add << std::endl; #endif liquid_levels_want[D_SELF] += add; total_level -= add; } #if LIQUID_DEBUG if (total_level > 0) infostream << " rest 3: " << " total_level=" << (int)total_level << std::endl; #endif for (u16 ii = 0; ii < 7; ii++) { // infinity and cave flood optimization if (neighbors[ii].infinity && liquid_levels_want[ii] < liquid_levels[ii]) { #if LIQUID_DEBUG infostream << " infinity: was=" << (int)ii << " = " << (int)liquid_levels_want[ii] << " to=" << (int)liquid_levels[ii] << std::endl; #endif regenerated += liquid_levels[ii] - liquid_levels_want[ii]; liquid_levels_want[ii] = liquid_levels[ii]; } else if ( liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < level_max && level_max > 1 && fast_flood && p0.Y < water_level && initial_size >= 1000 && ii != D_TOP && want_level >= level_max / 4 && can_liquid_same_level >= 5 && liquid_levels[D_TOP] >= level_max) { #if LIQUID_DEBUG infostream << " flood_fast: was=" << (int)ii << " = " << (int)liquid_levels_want[ii] << " to=" << (int)level_max << std::endl; #endif regenerated += level_max - liquid_levels_want[ii]; liquid_levels_want[ii] = level_max; } } #if LIQUID_DEBUG if (total_level != 0) //|| flowed != volume) infostream << " AFTER err level=" << (int)total_level //<< " flowed="<<flowed<< " volume=" << volume << " max=" << (int)level_max << " wantsame=" << (int)want_level << " top=" << (int)liquid_levels_want[D_TOP] << " topwas=" << (int)liquid_levels[D_TOP] << " bot=" << (int)liquid_levels_want[D_BOTTOM] << " botwas=" << (int)liquid_levels[D_BOTTOM] << std::endl; s16 flowed = 0; // for debug #endif #if LIQUID_DEBUG if (debug) infostream << " dpress=" << " bot=" << (int)liquid_levels_want[D_BOTTOM] << " slf=" << (int)liquid_levels_want[D_SELF] << " top=" << (int)liquid_levels_want[D_TOP] << std::endl; #endif for (u16 r = 0; r < 7; r++) { u16 i = liquid_random_map[(loopcount + loop_rand + 4) % 4][r]; if (liquid_levels_want[i] < 0 || !neighbors[i].liquid) continue; #if LIQUID_DEBUG if (debug) infostream << " set=" << i << " " << neighbors[i].pos << " want=" << (int)liquid_levels_want[i] << " was=" << (int) liquid_levels[i] << std::endl; #endif /* disabled because brokes constant volume of lava u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity; if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) { // amount to gain, limited by viscosity // must be at least 1 in absolute value s8 level_inc = liquid_levels_want[i] - liquid_levels[i]; if (level_inc < -viscosity || level_inc > viscosity) new_node_level = liquid_levels[i] + level_inc/viscosity; else if (level_inc < 0) new_node_level = liquid_levels[i] - 1; else if (level_inc > 0) new_node_level = liquid_levels[i] + 1; } else { */ // last level must flow down on stairs if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && (!neighbors[D_BOTTOM].liquid || level_max == 1) && liquid_levels_want[i] >= 1 && liquid_levels_want[i] <= 2) { for (u16 ir = D_SELF + 1; ir < D_TOP; ++ir) { // only same level u16 ii = liquid_random_map[(loopcount + loop_rand + 5) % 4][ir]; if (neighbors[ii].liquid) must_reflow_second.push_back(neighbors[i].pos + liquid_flow_dirs[ii]); //must_reflow_second[neighbors[i].pos + liquid_flow_dirs[ii]] = 1; } } #if LIQUID_DEBUG if (liquid_levels_want[i] > 0) flowed += liquid_levels_want[i]; #endif if (liquid_levels[i] == liquid_levels_want[i]) { continue; } if (neighbors[i].drop) {// && level_max > 1 && total_level >= level_max - 1 m_server->getEnv().getScriptIface()->node_drop(neighbors[i].pos, 2); } neighbors[i].node.setContent(liquid_kind_flowing); neighbors[i].node.setLevel(nodemgr, liquid_levels_want[i], 1); try { setNode(neighbors[i].pos, neighbors[i].node); } catch(InvalidPositionException &e) { verbosestream << "transformLiquidsReal: setNode() failed:" << neighbors[i].pos << ":" << e.what() << std::endl; } // If node emits light, MapBlock requires lighting update // or if node removed v3POS blockpos = getNodeBlockPos(neighbors[i].pos); MapBlock *block = getBlockNoCreateNoEx(blockpos, true); // remove true if light bugs if(block != NULL) { //modified_blocks[blockpos] = block; if(!nodemgr->get(neighbors[i].node).light_propagates || nodemgr->get(neighbors[i].node).light_source) // better to update always lighting_modified_blocks.set_try(block->getPos(), block); } // fmtodo: make here random %2 or.. if (total_level < level_max * can_liquid) must_reflow.push_back(neighbors[i].pos); } #if LIQUID_DEBUG //if (total_was != flowed) { if (total_was > flowed) { infostream << " volume changed! flowed=" << flowed << " total_was=" << total_was << " want_level=" << want_level; for (u16 rr = 0; rr <= 6; rr++) { infostream << " i=" << rr << ",b" << (int)liquid_levels[rr] << ",a" << (int)liquid_levels_want[rr]; } infostream << std::endl; } #endif /* //for better relax only same level if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { if (!neighbors[ii].l) continue; must_reflow.push_back(p0 + dirs[ii]); }*/ //g_profiler->graphAdd("liquids", 1); } u32 ret = loopcount >= initial_size ? 0 : transforming_liquid_size(); if (ret || loopcount > m_liquid_step_flow) m_liquid_step_flow += (m_liquid_step_flow > loopcount ? -1 : 1) * (int)loopcount / 10; /* if (loopcount) infostream<<"Map::transformLiquidsReal(): loopcount="<<loopcount<<" initial_size="<<initial_size <<" avgflow="<<m_liquid_step_flow <<" reflow="<<must_reflow.size() <<" reflow_second="<<must_reflow_second.size() <<" reflow_third="<<must_reflow_third.size() <<" queue="<< transforming_liquid_size() <<" per="<< porting::getTimeMs() - (end_ms - max_cycle_ms) <<" ret="<<ret<<std::endl; */ { //TimeTaker timer13("transformLiquidsReal() reflow"); //auto lock = m_transforming_liquid.lock_unique_rec(); std::lock_guard<std::mutex> lock(m_transforming_liquid_mutex); //m_transforming_liquid.insert(must_reflow.begin(), must_reflow.end()); for (const auto & p : must_reflow) m_transforming_liquid.push_back(p); must_reflow.clear(); //m_transforming_liquid.insert(must_reflow_second.begin(), must_reflow_second.end()); for (const auto & p : must_reflow_second) m_transforming_liquid.push_back(p); must_reflow_second.clear(); //m_transforming_liquid.insert(must_reflow_third.begin(), must_reflow_third.end()); for (const auto & p : must_reflow_third) m_transforming_liquid.push_back(p); must_reflow_third.clear(); } g_profiler->add("Server: liquids real processed", loopcount); if (regenerated) g_profiler->add("Server: liquids regenerated", regenerated); if (loopcount < initial_size) g_profiler->add("Server: liquids queue", initial_size); return loopcount; }
bool Map::propagateSunlight(v3POS pos, std::set<v3POS> & light_sources, bool remove_light) { MapBlock *block = getBlockNoCreateNoEx(pos); INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3POS pos_relative = block->getPosRelative(); for(s16 x = 0; x < MAP_BLOCKSIZE; ++x) { for(s16 z = 0; z < MAP_BLOCKSIZE; ++z) { bool no_sunlight = false; // Check if node above block has sunlight MapNode n = getNode(pos_relative + v3POS(x, MAP_BLOCKSIZE, z)); if (n) { if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } else { // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(block->getIsUnderground()) { no_sunlight = true; } else { MapNode n = block->getNode(v3POS(x, MAP_BLOCKSIZE - 1, z)); if(n && m_gamedef->ndef()->get(n).sunlight_propagates == false) no_sunlight = true; } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } s16 y = MAP_BLOCKSIZE - 1; // This makes difference to diminishing in water. //bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; --y) { v3POS pos(x, y, z); MapNode n = block->getNode(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. //stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); block->setNode(pos, n); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos); } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ if(block_below_is_valid) { MapNode n = getNode(pos_relative + v3POS(x, -1, z)); if (n) { if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } } else { // Just no block below, no need to panic. } } } } return block_below_is_valid; }
u32 Map::updateLighting(concurrent_map<v3POS, MapBlock*> & a_blocks, std::map<v3POS, MapBlock*> & modified_blocks, unsigned int max_cycle_ms) { INodeDefManager *nodemgr = m_gamedef->ndef(); int ret = 0; int loopcount = 0; TimeTaker timer("updateLighting"); // For debugging //bool debug=true; //u32 count_was = modified_blocks.size(); //std::unordered_set<v3POS, v3POSHash, v3POSEqual> light_sources; //std::unordered_map<v3POS, u8, v3POSHash, v3POSEqual> unlight_from_day, unlight_from_night; std::set<v3POS> light_sources; std::map<v3POS, u8> unlight_from_day, unlight_from_night; unordered_map_v3POS<int> processed; int num_bottom_invalid = 0; //MutexAutoLock lock2(m_update_lighting_mutex); #if !ENABLE_THREADS auto lock = m_nothread_locker.lock_unique_rec(); #endif { //TimeTaker t("updateLighting: first stuff"); u32 end_ms = porting::getTimeMs() + max_cycle_ms; for(auto i = a_blocks.begin(); i != a_blocks.end(); ++i) { auto block = getBlockNoCreateNoEx(i->first); for(;;) { // Don't bother with dummy blocks. if(!block || block->isDummy()) break; auto lock = block->try_lock_unique_rec(); if (!lock->owns_lock()) break; // may cause dark areas v3POS pos = block->getPos(); if (processed.count(pos) && processed[pos] <= i->first.Y ) { break; } ++loopcount; processed[pos] = i->first.Y; v3POS posnodes = block->getPosRelative(); //modified_blocks[pos] = block; block->setLightingExpired(true); block->lighting_broken = true; /* Clear all light from block */ for(s16 z = 0; z < MAP_BLOCKSIZE; z++) for(s16 x = 0; x < MAP_BLOCKSIZE; x++) for(s16 y = 0; y < MAP_BLOCKSIZE; y++) { v3POS p(x, y, z); bool is_valid_position; MapNode n = block->getNode(p, &is_valid_position); if (!is_valid_position) { /* This would happen when dealing with a dummy block. */ infostream << "updateLighting(): InvalidPositionException" << std::endl; continue; } u8 oldlight_day = n.getLight(LIGHTBANK_DAY, nodemgr); u8 oldlight_night = n.getLight(LIGHTBANK_NIGHT, nodemgr); n.setLight(LIGHTBANK_DAY, 0, nodemgr); n.setLight(LIGHTBANK_NIGHT, 0, nodemgr); block->setNode(p, n); // If node sources light, add to list //u8 source = nodemgr->get(n).light_source; if(nodemgr->get(n).light_source) light_sources.insert(p + posnodes); v3POS p_map = 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) { if(oldlight_day) unlight_from_day[p_map] = oldlight_day; if(oldlight_night) unlight_from_night[p_map] = oldlight_night; } } lock->unlock(); bool bottom_valid = propagateSunlight(pos, light_sources); if(!bottom_valid) num_bottom_invalid++; pos.Y--; block = getBlockNoCreateNoEx(pos); } if (porting::getTimeMs() > end_ms) { ++ret; break; } } } { //TimeTaker timer("updateLighting: unspreadLight"); unspreadLight(LIGHTBANK_DAY, unlight_from_day, light_sources, modified_blocks); unspreadLight(LIGHTBANK_NIGHT, unlight_from_night, light_sources, modified_blocks); } { //TimeTaker timer("updateLighting: spreadLight"); spreadLight(LIGHTBANK_DAY, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10); spreadLight(LIGHTBANK_NIGHT, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10); } for (auto & i : processed) { a_blocks.erase(i.first); MapBlock *block = getBlockNoCreateNoEx(i.first); if(!block) continue; block->setLightingExpired(false); block->lighting_broken = false; } g_profiler->add("Server: light blocks", loopcount); return ret; }