void reset() { if(owns_lock()) { ptr->unlock(); IsLock=false; } ptr=0; }
//TODO: REMOVE THIS func and use Map::getBlock MapBlock* Map::getBlockNoCreateNoEx(v3POS p, bool trylock, bool nocache) { #ifndef NDEBUG ScopeProfiler sp(g_profiler, "Map: getBlock"); #endif #if !ENABLE_THREADS nocache = true; //very dirty hack. fix and remove. Also compare speed: no cache and cache with lock #endif if (!nocache) { #if ENABLE_THREADS && !HAVE_THREAD_LOCAL auto lock = try_shared_lock(m_block_cache_mutex, TRY_TO_LOCK); if(lock.owns_lock()) #endif if(m_block_cache && p == m_block_cache_p) { #ifndef NDEBUG g_profiler->add("Map: getBlock cache hit", 1); #endif return m_block_cache; } } MapBlockP block; { auto lock = trylock ? m_blocks.try_lock_shared_rec() : m_blocks.lock_shared_rec(); if (!lock->owns_lock()) return nullptr; auto n = m_blocks.find(p); if(n == m_blocks.end()) return nullptr; block = n->second; } if (!nocache) { #if ENABLE_THREADS && !HAVE_THREAD_LOCAL auto lock = unique_lock(m_block_cache_mutex, TRY_TO_LOCK); if(lock.owns_lock()) #endif { m_block_cache_p = p; m_block_cache = block; } } return block; }
void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, u32 active_object_count, u32 active_object_count_wider, MapNode neighbor, bool activate) { GameScripting *scriptIface = env->getScriptIface(); auto _script_lock = RecursiveMutexAutoLock(scriptIface->m_luastackmutex, std::try_to_lock); if (!_script_lock.owns_lock()) { return; } scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); sanity_check(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); int error_handler = PUSH_ERROR_HANDLER(L); // Get registered_abms lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_abms"); luaL_checktype(L, -1, LUA_TTABLE); lua_remove(L, -2); // Remove core // Get registered_abms[m_id] lua_pushnumber(L, m_id); lua_gettable(L, -2); if(lua_isnil(L, -1)) //FATAL_ERROR(""); return; lua_remove(L, -2); // Remove registered_abms scriptIface->setOriginFromTable(-1); // Call action luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "action"); luaL_checktype(L, -1, LUA_TFUNCTION); lua_remove(L, -2); // Remove registered_abms[m_id] push_v3s16(L, p); pushnode(L, n, env->getGameDef()->ndef()); lua_pushnumber(L, active_object_count); lua_pushnumber(L, active_object_count_wider); pushnode(L, neighbor, env->getGameDef()->ndef()); lua_pushboolean(L, activate); int result = lua_pcall(L, 6, 0, error_handler); if (result) scriptIface->scriptError(result, "LuaABM::trigger"); lua_pop(L, 1); // Pop error handler }
void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) { GameScripting *scriptIface = env->getScriptIface(); auto _script_lock = RecursiveMutexAutoLock(scriptIface->m_luastackmutex, std::try_to_lock); if (!_script_lock.owns_lock()) { return; } scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); sanity_check(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); int error_handler = PUSH_ERROR_HANDLER(L); // Get registered_lbms lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_lbms"); luaL_checktype(L, -1, LUA_TTABLE); lua_remove(L, -2); // Remove core // Get registered_lbms[m_id] lua_pushnumber(L, m_id); lua_gettable(L, -2); //FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); if (lua_isnil(L, -1)) { errorstream << "Entry with given id " << m_id << " not found in registered_lbms table" << std::endl; return; } lua_remove(L, -2); // Remove registered_lbms scriptIface->setOriginFromTable(-1); // Call action luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "action"); luaL_checktype(L, -1, LUA_TFUNCTION); lua_remove(L, -2); // Remove registered_lbms[m_id] push_v3s16(L, p); pushnode(L, n, env->getGameDef()->ndef()); int result = lua_pcall(L, 2, 0, error_handler); if (result) scriptIface->scriptError(result, "LuaLBM::trigger"); lua_pop(L, 1); // Pop error handler }
MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position) { if (isValidPosition(p) == false) return m_parent->getNodeTry(getPosRelative() + p); if (data == NULL) { if (is_valid_position) *is_valid_position = false; return MapNode(CONTENT_IGNORE); } auto lock = try_lock_shared_rec(); if (!lock->owns_lock()) { if (is_valid_position) *is_valid_position = false; return MapNode(CONTENT_IGNORE); } if (is_valid_position) *is_valid_position = true; return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; }
~elliptics_unique_lock() { if (owns_lock()) unlock(); }
void ClientMap::updateDrawList(video::IVideoDriver* driver, float dtime, unsigned int max_cycle_ms) { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); //g_profiler->add("CM::updateDrawList() count", 1); TimeTaker timer_step("ClientMap::updateDrawList"); INodeDefManager *nodemgr = m_gamedef->ndef(); if (!m_drawlist_last) m_drawlist_current = !m_drawlist_current; auto & drawlist = m_drawlist_current ? m_drawlist_1 : m_drawlist_0; if (!max_cycle_ms) max_cycle_ms = 300/getControl().fps_wanted; m_camera_mutex.Lock(); v3f camera_position = m_camera_position; f32 camera_fov = m_camera_fov; //v3s16 camera_offset = m_camera_offset; m_camera_mutex.Unlock(); // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. // Or maybe they aren't? Well whatever. camera_fov *= 1.2; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1); v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d; v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; // Take a fair amount as we will be dropping more out later // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( p_nodes_min.X / MAP_BLOCKSIZE - 3, p_nodes_min.Y / MAP_BLOCKSIZE - 3, p_nodes_min.Z / MAP_BLOCKSIZE - 3); v3s16 p_blocks_max( p_nodes_max.X / MAP_BLOCKSIZE + 1, p_nodes_max.Y / MAP_BLOCKSIZE + 1, p_nodes_max.Z / MAP_BLOCKSIZE + 1); // Number of blocks in rendering range u32 blocks_in_range = 0; // Number of blocks occlusion culled u32 blocks_occlusion_culled = 0; // Number of blocks in rendering range but don't have a mesh u32 blocks_in_range_without_mesh = 0; // Blocks that had mesh that would have been drawn according to // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; // Blocks which had a corresponding meshbuffer for this pass //u32 blocks_had_pass_meshbuf = 0; // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; // Distance to farthest drawn block float farthest_drawn = 0; int m_mesh_queued = 0; bool free_move = g_settings->getBool("free_move"); float range_max = 100000 * BS; if(m_control.range_all == false) range_max = m_control.wanted_range * BS; if (draw_nearest.empty()) { //ScopeProfiler sp(g_profiler, "CM::updateDrawList() make list", SPT_AVG); TimeTaker timer_step("ClientMap::updateDrawList make list"); auto lock = m_blocks.try_lock_shared_rec(); if (!lock->owns_lock()) return; draw_nearest.clear(); for(auto & ir : m_blocks) { auto bp = ir.first; if(m_control.range_all == false) { if(bp.X < p_blocks_min.X || bp.X > p_blocks_max.X || bp.Z > p_blocks_max.Z || bp.Z < p_blocks_min.Z || bp.Y < p_blocks_min.Y || bp.Y > p_blocks_max.Y) continue; } v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE; // Block center position v3f blockpos( ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS, ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS, ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS ); f32 d = radius_box(blockpos, camera_position); //blockpos_relative.getLength(); if (d> range_max) continue; int range = d / (MAP_BLOCKSIZE * BS); draw_nearest.emplace_back(std::make_pair(bp, range)); } } const int maxq = 1000; // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if(free_move) { MapNode n = getNodeNoEx(cam_pos_nodes); if(n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } u32 calls = 0, end_ms = porting::getTimeMs() + u32(max_cycle_ms); std::unordered_map<v3POS, bool, v3POSHash, v3POSEqual> occlude_cache; while (!draw_nearest.empty()) { auto ir = draw_nearest.back(); auto bp = ir.first; int range = ir.second; draw_nearest.pop_back(); ++calls; //auto block = getBlockNoCreateNoEx(bp); auto block = m_blocks.get(bp); if (!block) continue; int mesh_step = getFarmeshStep(m_control, getNodeBlockPos(cam_pos_nodes), bp); /* Compare block position to camera position, skip if not seen on display */ auto mesh = block->getMesh(mesh_step); if (mesh) mesh->updateCameraOffset(m_camera_offset); blocks_in_range++; auto smesh_size = block->mesh_size; /* Ignore if mesh doesn't exist */ { if(!mesh) { blocks_in_range_without_mesh++; if (m_mesh_queued < maxq || range <= 2) { m_client->addUpdateMeshTask(bp, false); ++m_mesh_queued; } continue; } if(mesh_step == mesh->step && block->getTimestamp() <= mesh->timestamp && !smesh_size) { blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ v3s16 cpn = bp * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = BS*1; float stepfac = 1.2; float startoff = BS*1; float endoff = -BS*MAP_BLOCKSIZE; //*1.42; //*1.42; v3s16 spn = cam_pos_nodes + v3s16(0,0,0); s16 bs2 = MAP_BLOCKSIZE/2 + 1; u32 needed_count = 1; if( range > 1 && smesh_size && occlusion_culling_enabled && isOccluded(this, spn, cpn + v3s16(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) ) { blocks_occlusion_culled++; continue; } // This block is in range. Reset usage timer. block->resetUsageTimer(); // Limit block count in case of a sudden increase blocks_would_have_drawn++; /* if(blocks_drawn >= m_control.wanted_max_blocks && m_control.range_all == false && d > m_control.wanted_min_range * BS) continue; */ if (mesh_step != mesh->step && (m_mesh_queued < maxq*1.2 || range <= 2)) { m_client->addUpdateMeshTask(bp); ++m_mesh_queued; } if (block->getTimestamp() > mesh->timestamp + (smesh_size ? 0 : range >= 1 ? 60 : 5) && (m_mesh_queued < maxq*1.5 || range <= 2)) { m_client->addUpdateMeshTaskWithEdge(bp); ++m_mesh_queued; } if(!smesh_size) continue; mesh->incrementUsageTimer(dtime); // Add to set //block->refGrab(); block->resetUsageTimer(); drawlist.set(bp, block); blocks_drawn++; if(range * MAP_BLOCKSIZE > farthest_drawn) farthest_drawn = range * MAP_BLOCKSIZE; if (farthest_drawn > m_control.farthest_drawn) m_control.farthest_drawn = farthest_drawn; if (porting::getTimeMs() > end_ms) { break; } } m_drawlist_last = draw_nearest.size(); //if (m_drawlist_last) infostream<<"breaked UDL "<<m_drawlist_last<<" collected="<<drawlist.size()<<" calls="<<calls<<" s="<<m_blocks.size()<<" maxms="<<max_cycle_ms<<" fw="<<getControl().fps_wanted<<" morems="<<porting::getTimeMs() - end_ms<< " meshq="<<m_mesh_queued<<" occache="<<occlude_cache.size()<<std::endl; if (m_drawlist_last) return; //for (auto & ir : *m_drawlist) // ir.second->refDrop(); auto m_drawlist_old = !m_drawlist_current ? &m_drawlist_1 : &m_drawlist_0; m_drawlist = m_drawlist_current ? &m_drawlist_1 : &m_drawlist_0; m_drawlist_old->clear(); m_control.blocks_would_have_drawn = blocks_would_have_drawn; m_control.blocks_drawn = blocks_drawn; m_control.farthest_drawn = farthest_drawn; g_profiler->avg("CM: blocks total", m_blocks.size()); g_profiler->avg("CM: blocks in range", blocks_in_range); g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); if(blocks_in_range != 0) g_profiler->avg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh/blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); g_profiler->avg("CM: farthest drawn", farthest_drawn); g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); }
~unique_lock() { if(owns_lock()) mutex_.unlock(); }
void PlayerSAO::step(float dtime, bool send_recommended) { if (!m_player) return; if(!m_properties_sent) { std::string str = getPropertyPacket(); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); m_properties_sent = true; } // If attached, check that our parent is still there. If it isn't, detach. if(m_attachment_parent_id && !isAttached()) { { auto lock = try_lock_unique_rec(); if (!lock->owns_lock()) goto NOLOCK1; m_attachment_parent_id = 0; m_attachment_bone = ""; m_attachment_position = v3f(0,0,0); m_attachment_rotation = v3f(0,0,0); } setBasePosition(m_last_good_position); ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); } NOLOCK1:; //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl; // Set lag pool maximums based on estimated lag const float LAG_POOL_MIN = 5.0; float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; if(lag_pool_max < LAG_POOL_MIN) lag_pool_max = LAG_POOL_MIN; m_dig_pool.setMax(lag_pool_max); m_move_pool.setMax(lag_pool_max); // Increment cheat prevention timers m_dig_pool.add(dtime); m_move_pool.add(dtime); { auto lock = try_lock_unique_rec(); if (!lock->owns_lock()) goto NOLOCK2; m_time_from_last_punch += dtime; m_nocheat_dig_time += dtime; m_ms_from_last_respawn += dtime*1000; } NOLOCK2:; // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally // If the object gets detached this comes into effect automatically from the last known origin if (isAttached()) { v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); m_last_good_position = pos; setBasePosition(pos); } if (!send_recommended) return; // If the object is attached client-side, don't waste bandwidth sending its position to clients if(m_position_not_sent && !isAttached()) { m_position_not_sent = false; float update_interval = m_env->getSendRecommendedInterval(); v3f pos, vel, acc; if(isAttached()) // Just in case we ever do send attachment position too pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); else { pos = m_base_position + v3f(0,BS*1,0); vel = m_player->getSpeed(); } std::string str = gob_cmd_update_position( pos, vel, acc, m_yaw, true, false, update_interval ); // create message and add to list ActiveObjectMessage aom(getId(), false, str); m_messages_out.push(aom); } if (!m_armor_groups_sent) { m_armor_groups_sent = true; std::string str = gob_cmd_update_armor_groups( m_armor_groups); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } if (!m_physics_override_sent) { auto lock = try_lock_unique_rec(); if (!lock->owns_lock()) goto NOLOCK3; m_physics_override_sent = true; std::string str = gob_cmd_update_physics_override(m_physics_override_speed, m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak, m_physics_override_sneak_glitch); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } NOLOCK3:; if (!m_animation_sent) { auto lock = try_lock_unique_rec(); if (!lock->owns_lock()) goto NOLOCK4; m_animation_sent = true; std::string str = gob_cmd_update_animation( m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } NOLOCK4:; if (!m_bone_position_sent) { m_bone_position_sent = true; for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } } if (!m_attachment_sent){ m_attachment_sent = true; std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation); // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } }
operator bool() const{ return owns_lock(); }
~lock_guard(){if(owns_lock())ptr->unlock();}
int RemoteClient::GetNextBlocks ( ServerEnvironment *env, EmergeManager * emerge, float dtime, double m_uptime, std::vector<PrioritySortedBlockTransfer> &dest) { DSTACK(FUNCTION_NAME); auto lock = lock_unique_rec(); if (!lock->owns_lock()) return 0; // Increment timers m_nothing_to_send_pause_timer -= dtime; m_nearest_unsent_reset_timer += dtime; m_time_from_building += dtime; if (m_nearest_unsent_reset) { m_nearest_unsent_reset = 0; m_nearest_unsent_reset_timer = 999; m_nothing_to_send_pause_timer = 0; m_time_from_building = 999; } if(m_nothing_to_send_pause_timer >= 0) return 0; Player *player = env->getPlayer(peer_id); // This can happen sometimes; clients and players are not in perfect sync. if(player == NULL) return 0; v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); if(playerspeed.getLength() > 1000.0*BS) //cheater or bug, ignore him return 0; v3f playerspeeddir(0,0,0); if(playerspeed.getLength() > 1.0*BS) playerspeeddir = playerspeed / playerspeed.getLength(); // Predict to next block v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); // Camera position and direction v3f camera_pos = player->getEyePosition(); v3f camera_dir = v3f(0,0,1); camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); //infostream<<"camera_dir=("<<camera_dir<<")"<< " camera_pos="<<camera_pos<<std::endl; /* Get the starting value of the block finder radius. */ if(m_last_center != center) { m_last_center = center; m_nearest_unsent_reset_timer = 999; } if (m_last_direction.getDistanceFrom(camera_dir)>0.4) { // 1 = 90deg m_last_direction = camera_dir; m_nearest_unsent_reset_timer = 999; } /*infostream<<"m_nearest_unsent_reset_timer=" <<m_nearest_unsent_reset_timer<<std::endl;*/ // Reset periodically to workaround for some bugs or stuff if(m_nearest_unsent_reset_timer > 120.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; m_nearest_unsent_reset = 0; //infostream<<"Resetting m_nearest_unsent_d for "<<peer_id<<std::endl; } //s16 last_nearest_unsent_d = m_nearest_unsent_d; s16 d_start = m_nearest_unsent_d; //infostream<<"d_start="<<d_start<<std::endl; static const u16 max_simul_sends_setting = g_settings->getU16 ("max_simultaneous_block_sends_per_client"); static const u16 max_simul_sends_usually = max_simul_sends_setting; /* Check the time from last addNode/removeNode. Decrease send rate if player is building stuff. */ static const auto full_block_send_enable_min_time_from_building = g_settings->getFloat("full_block_send_enable_min_time_from_building"); if(m_time_from_building < full_block_send_enable_min_time_from_building) { /* max_simul_sends_usually = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; */ if(d_start<=1) d_start=2; m_nearest_unsent_reset_timer = 999; //magical number more than ^ other number 120 - need to reset d on next iteration } /* Number of blocks sending + number of blocks selected for sending */ u32 num_blocks_selected = 0; u32 num_blocks_sending = 0; /* next time d will be continued from the d from which the nearest unsent block was found this time. This is because not necessarily any of the blocks found this time are actually sent. */ s32 new_nearest_unsent_d = -1; static const auto max_block_send_distance = g_settings->getS16("max_block_send_distance"); s16 full_d_max = max_block_send_distance; if (wanted_range) { s16 wanted_blocks = wanted_range / MAP_BLOCKSIZE + 1; if (wanted_blocks < full_d_max) full_d_max = wanted_blocks; } s16 d_max = full_d_max; static const s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); // Don't loop very much at a time s16 max_d_increment_at_time = 10; if(d_max > d_start + max_d_increment_at_time) d_max = d_start + max_d_increment_at_time; /*if(d_max_gen > d_start+2) d_max_gen = d_start+2;*/ //infostream<<"Starting from "<<d_start<<std::endl; s32 nearest_emerged_d = -1; s32 nearest_emergefull_d = -1; s32 nearest_sent_d = -1; //bool queue_is_full = false; f32 speed_in_blocks = (playerspeed/(MAP_BLOCKSIZE*BS)).getLength(); int blocks_occlusion_culled = 0; static const bool server_occlusion = g_settings->getBool("server_occlusion"); bool occlusion_culling_enabled = server_occlusion; auto cam_pos_nodes = floatToInt(playerpos, BS); auto nodemgr = env->getGameDef()->getNodeDefManager(); MapNode n; { #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif n = env->getMap().getNodeTry(cam_pos_nodes); } if(n && nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; unordered_map_v3POS<bool> occlude_cache; s16 d; for(d = d_start; d <= d_max; d++) { /*errorstream<<"checking d="<<d<<" for " <<server->getPlayerName(peer_id)<<std::endl;*/ //infostream<<"RemoteClient::SendBlocks(): d="<<d<<" d_start="<<d_start<<" d_max="<<d_max<<" d_max_gen="<<d_max_gen<<std::endl; std::vector<v3POS> list; if (d > 2 && d == d_start && m_nearest_unsent_reset_timer != 999) { // oops, again magic number from up ^ list.push_back(v3POS(0,0,0)); } bool can_skip = d > 1; // Fast fall/move optimize. speed_in_blocks now limited to 6.4 if (speed_in_blocks>0.8 && d <= 2) { can_skip = false; if (d == 0) { for(s16 addn = 0; addn < (speed_in_blocks+1)*2; ++addn) list.push_back(floatToInt(playerspeeddir*addn, 1)); } else if (d == 1) { for(s16 addn = 0; addn < (speed_in_blocks+1)*1.5; ++addn) { list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 0, 0, 1)); // back list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, 0)); // left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, 0)); // right list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 0, 0, -1)); // front } } else if (d == 2) { for(s16 addn = 0; addn < (speed_in_blocks+1)*1.5; ++addn) { list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, 1)); // back left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, 1)); // left right list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, -1)); // right left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, -1)); // front right } } } else { /* Get the border/face dot coordinates of a "d-radiused" box */ list = FacePositionCache::getFacePositions(d); } for(auto li=list.begin(); li!=list.end(); ++li) { v3POS p = *li + center; /* Send throttling - Don't allow too many simultaneous transfers - EXCEPT when the blocks are very close Also, don't send blocks that are already flying. */ // Start with the usual maximum u16 max_simul_dynamic = max_simul_sends_usually; // If block is very close, allow full maximum if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) max_simul_dynamic = max_simul_sends_setting; // Don't select too many blocks for sending if (num_blocks_selected + num_blocks_sending >= max_simul_dynamic) { //queue_is_full = true; goto queue_full_break; } /* Do not go over-limit */ if (blockpos_over_limit(p)) continue; // If this is true, inexistent block will be made from scratch bool generate = d <= d_max_gen; { /*// Limit the generating area vertically to 2/3 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) generate = false;*/ /* maybe good idea (if not use block culling) but brokes far (25+) area generate by flooding emergequeue with no generate blocks // Limit the send area vertically to 1/2 if(can_skip && abs(p.Y - center.Y) > full_d_max / 2) generate = false; */ } //infostream<<"d="<<d<<std::endl; /* Don't generate or send if not in sight FIXME This only works if the client uses a small enough FOV setting. The default of 72 degrees is fine. */ float camera_fov = ((fov+5)*M_PI/180) * 4./3.; if(can_skip && isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false) { continue; } /* Don't send already sent blocks */ unsigned int block_sent = 0; { auto lock = m_blocks_sent.lock_shared_rec(); block_sent = m_blocks_sent.find(p) != m_blocks_sent.end() ? m_blocks_sent.get(p) : 0; } if(block_sent > 0 && (/* (block_overflow && d>1) || */ block_sent + (d <= 2 ? 1 : d*d*d) > m_uptime)) { continue; } /* Check if map has this block */ MapBlock *block; { #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif block = env->getMap().getBlockNoCreateNoEx(p); } bool surely_not_found_on_disk = false; bool block_is_invalid = false; if(block != NULL) { if (d > 3 && block->content_only == CONTENT_AIR) { continue; } if (block_sent > 0 && block_sent >= block->m_changed_timestamp) { continue; } if (occlusion_culling_enabled) { ScopeProfiler sp(g_profiler, "SMap: Occusion calls"); //Occlusion culling auto cpn = p*MAP_BLOCKSIZE; // No occlusion culling when free_move is on and camera is // inside ground cpn += v3POS(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = 1; float stepfac = 1.3; float startoff = 5; float endoff = -MAP_BLOCKSIZE; v3POS spn = cam_pos_nodes + v3POS(0,0,0); s16 bs2 = MAP_BLOCKSIZE/2 + 1; u32 needed_count = 1; #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif //VERY BAD COPYPASTE FROM clientmap.cpp! if( d >= 1 && occlusion_culling_enabled && isOccluded(&env->getMap(), spn, cpn + v3POS(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) ) { //infostream<<" occlusion player="<<cam_pos_nodes<<" d="<<d<<" block="<<cpn<<" total="<<blocks_occlusion_culled<<"/"<<num_blocks_selected<<std::endl; g_profiler->add("SMap: Occlusion skip", 1); blocks_occlusion_culled++; continue; } } // Reset usage timer, this block will be of use in the future. block->resetUsageTimer(); if (block->getLightingExpired()) { //env->getServerMap().lighting_modified_blocks.set(p, nullptr); env->getServerMap().lighting_modified_add(p, d); } if (block->lighting_broken > 0 && (block_sent || d > 0)) continue; // Block is valid if lighting is up-to-date and data exists if(block->isValid() == false) { block_is_invalid = true; } if(block->isGenerated() == false) { continue; } /* If block is not close, don't send it unless it is near ground level. Block is near ground level if night-time mesh differs from day-time mesh. */ /* if(d >= 4) { if(block->getDayNightDiff() == false) continue; } */ } /* If block has been marked to not exist on disk (dummy) and generating new ones is not wanted, skip block. */ if(generate == false && surely_not_found_on_disk == true) { // get next one. continue; } /* Add inexistent block to emerge queue. */ if(block == NULL || surely_not_found_on_disk || block_is_invalid) { //infostream<<"start gen d="<<d<<" p="<<p<<" notfound="<<surely_not_found_on_disk<<" invalid="<< block_is_invalid<<" block="<<block<<" generate="<<generate<<std::endl; if (emerge->enqueueBlockEmerge(peer_id, p, generate)) { if (nearest_emerged_d == -1) nearest_emerged_d = d; } else { if (nearest_emergefull_d == -1) nearest_emergefull_d = d; goto queue_full_break; } // get next one. continue; } if(nearest_sent_d == -1) nearest_sent_d = d; /* Add block to send queue */ PrioritySortedBlockTransfer q((float)d, p, peer_id); dest.push_back(q); num_blocks_selected += 1; } } queue_full_break: //infostream<<"Stopped at "<<d<<" d_start="<<d_start<< " d_max="<<d_max<<" nearest_emerged_d="<<nearest_emerged_d<<" nearest_emergefull_d="<<nearest_emergefull_d<< " new_nearest_unsent_d="<<new_nearest_unsent_d<< " sel="<<num_blocks_selected<< "+"<<num_blocks_sending << " culled=" << blocks_occlusion_culled <<" cEN="<<occlusion_culling_enabled<<std::endl; num_blocks_selected += num_blocks_sending; if(!num_blocks_selected && d_start <= d) { //new_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 1.0; } // If nothing was found for sending and nothing was queued for // emerging, continue next time browsing from here if(nearest_emerged_d != -1){ new_nearest_unsent_d = nearest_emerged_d; } else if(nearest_emergefull_d != -1){ new_nearest_unsent_d = nearest_emergefull_d; } else { if(d > full_d_max){ new_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 1.0; } else { if(nearest_sent_d != -1) new_nearest_unsent_d = nearest_sent_d; else new_nearest_unsent_d = d; } } if(new_nearest_unsent_d != -1) m_nearest_unsent_d = new_nearest_unsent_d; return num_blocks_selected - num_blocks_sending; }
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; }
u32 Map::timerUpdate(float uptime, float unload_timeout, u32 max_loaded_blocks, unsigned int max_cycle_ms, std::vector<v3POS> *unloaded_blocks) { bool save_before_unloading = (mapType() == MAPTYPE_SERVER); // Profile modified reasons Profiler modprofiler; if (/*!m_blocks_update_last && */ m_blocks_delete->size() > 1000) { m_blocks_delete = (m_blocks_delete == &m_blocks_delete_1 ? &m_blocks_delete_2 : &m_blocks_delete_1); verbosestream << "Deleting blocks=" << m_blocks_delete->size() << std::endl; for (auto & ir : *m_blocks_delete) delete ir.first; m_blocks_delete->clear(); getBlockCacheFlush(); } u32 deleted_blocks_count = 0; u32 saved_blocks_count = 0; u32 block_count_all = 0; u32 n = 0, calls = 0, end_ms = porting::getTimeMs() + max_cycle_ms; std::vector<MapBlockP> blocks_delete; int save_started = 0; { auto lock = m_blocks.try_lock_shared_rec(); if (!lock->owns_lock()) return m_blocks_update_last; #if !ENABLE_THREADS auto lock_map = m_nothread_locker.try_lock_unique_rec(); if (!lock_map->owns_lock()) return m_blocks_update_last; #endif for(auto ir : m_blocks) { if (n++ < m_blocks_update_last) { continue; } else { m_blocks_update_last = 0; } ++calls; auto block = ir.second; if (!block) continue; { auto lock = block->try_lock_unique_rec(); if (!lock->owns_lock()) continue; if(block->getUsageTimer() > unload_timeout) { // block->refGet() <= 0 && v3POS p = block->getPos(); //infostream<<" deleting block p="<<p<<" ustimer="<<block->getUsageTimer() <<" to="<< unload_timeout<<" inc="<<(uptime - block->m_uptime_timer_last)<<" state="<<block->getModified()<<std::endl; // Save if modified if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) { //modprofiler.add(block->getModifiedReasonString(), 1); if(!save_started++) beginSave(); if (!saveBlock(block)) continue; saved_blocks_count++; } blocks_delete.push_back(block); if(unloaded_blocks) unloaded_blocks->push_back(p); deleted_blocks_count++; } else { #ifndef SERVER if (block->mesh_old) block->mesh_old = nullptr; #endif if (!block->m_uptime_timer_last) // not very good place, but minimum modifications block->m_uptime_timer_last = uptime - 0.1; block->incrementUsageTimer(uptime - block->m_uptime_timer_last); block->m_uptime_timer_last = uptime; block_count_all++; /*#ifndef SERVER if(block->refGet() == 0 && block->getUsageTimer() > g_settings->getFloat("unload_unused_meshes_timeout")) { if(block->mesh){ delete block->mesh; block->mesh = NULL; } } #endif*/ } } // block lock if (porting::getTimeMs() > end_ms) { m_blocks_update_last = n; break; } } } if(save_started) endSave(); if (!calls) m_blocks_update_last = 0; for (auto & block : blocks_delete) this->deleteBlock(block); // Finally delete the empty sectors if(deleted_blocks_count != 0) { if (m_blocks_update_last) infostream << "ServerMap: timerUpdate(): Blocks processed:" << calls << "/" << m_blocks.size() << " to " << m_blocks_update_last << std::endl; PrintInfo(infostream); // ServerMap/ClientMap: infostream << "Unloaded " << deleted_blocks_count << "/" << (block_count_all + deleted_blocks_count) << " blocks from memory"; infostream << " (deleteq1=" << m_blocks_delete_1.size() << " deleteq2=" << m_blocks_delete_2.size() << ")"; if(saved_blocks_count) infostream << ", of which " << saved_blocks_count << " were written"; /* infostream<<", "<<block_count_all<<" blocks in memory"; */ infostream << "." << std::endl; if(saved_blocks_count != 0) { PrintInfo(infostream); // ServerMap/ClientMap: //infostream<<"Blocks modified by: "<<std::endl; modprofiler.print(infostream); } } return m_blocks_update_last; }