diff options
Diffstat (limited to 'src/server.cpp')
-rw-r--r-- | src/server.cpp | 720 |
1 files changed, 382 insertions, 338 deletions
diff --git a/src/server.cpp b/src/server.cpp index b3fb037b5..93767da9d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -136,24 +136,31 @@ void *ServerThread::run() return nullptr; } -v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const +v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const { - if(pos_exists) *pos_exists = false; - switch(type){ - case SSP_LOCAL: + if (pos_exists) + *pos_exists = false; + + switch (type ){ + case SoundLocation::Local: return v3f(0,0,0); - case SSP_POSITIONAL: - if(pos_exists) *pos_exists = true; + case SoundLocation::Position: + if (pos_exists) + *pos_exists = true; return pos; - case SSP_OBJECT: { - if(object == 0) - return v3f(0,0,0); - ServerActiveObject *sao = env->getActiveObject(object); - if(!sao) - return v3f(0,0,0); - if(pos_exists) *pos_exists = true; - return sao->getBasePosition(); } + case SoundLocation::Object: + { + if (object == 0) + return v3f(0,0,0); + ServerActiveObject *sao = env->getActiveObject(object); + if (!sao) + return v3f(0,0,0); + if (pos_exists) + *pos_exists = true; + return sao->getBasePosition(); + } } + return v3f(0,0,0); } @@ -254,7 +261,7 @@ Server::Server( #if USE_PROMETHEUS m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend()); #else - m_metrics_backend = std::unique_ptr<MetricsBackend>(new MetricsBackend()); + m_metrics_backend = std::make_unique<MetricsBackend>(); #endif m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)"); @@ -268,9 +275,15 @@ Server::Server( "minetest_core_latency", "Latency value (in seconds)"); - m_aom_buffer_counter = m_metrics_backend->addCounter( - "minetest_core_aom_generated_count", - "Number of active object messages generated"); + + const std::string aom_types[] = {"reliable", "unreliable"}; + for (u32 i = 0; i < ARRLEN(aom_types); i++) { + std::string help_str("Number of active object messages generated ("); + help_str.append(aom_types[i]).append(")"); + m_aom_buffer_counter[i] = m_metrics_backend->addCounter( + "minetest_core_aom_generated_count", help_str, + {{"type", aom_types[i]}}); + } m_packet_recv_counter = m_metrics_backend->addCounter( "minetest_core_server_packet_recv", @@ -280,6 +293,10 @@ Server::Server( "minetest_core_server_packet_recv_processed", "Valid received packets processed"); + m_map_edit_event_counter = m_metrics_backend->addCounter( + "minetest_core_map_edit_events", + "Number of map edit events"); + m_lag_gauge->set(g_settings->getFloat("dedicated_server_step")); } @@ -396,7 +413,7 @@ void Server::init() } // Create emerge manager - m_emerge = new EmergeManager(this); + m_emerge = new EmergeManager(this, m_metrics_backend.get()); // Create ban manager std::string ban_path = m_path_world + DIR_DELIM "ipban.txt"; @@ -406,11 +423,16 @@ void Server::init() m_mod_storage_database = openModStorageDatabase(m_path_world); m_mod_storage_database->beginSave(); - m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world)); + m_modmgr = std::make_unique<ServerModManager>(m_path_world); std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods(); + // complain about mods with unsatisfied dependencies if (!m_modmgr->isConsistent()) { m_modmgr->printUnsatisfiedModsError(); + + warningstream + << "You have unsatisfied dependencies, loading your world anyway. " + << "This will become a fatal error in the future." << std::endl; } //lock environment @@ -426,10 +448,11 @@ void Server::init() m_script = new ServerScripting(this); // Must be created before mod loading because we have some inventory creation - m_inventory_mgr = std::unique_ptr<ServerInventoryManager>(new ServerInventoryManager()); + m_inventory_mgr = std::make_unique<ServerInventoryManager>(); m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); + m_gamespec.checkAndLog(); m_modmgr->loadMods(m_script); // Read Textures and calculate sha1 sums @@ -461,7 +484,8 @@ void Server::init() // Initialize Environment m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment - m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); + m_env = new ServerEnvironment(servermap, m_script, this, + m_path_world, m_metrics_backend.get()); m_inventory_mgr->setEnv(m_env); m_clients.setEnv(m_env); @@ -480,6 +504,9 @@ void Server::init() // Give environment reference to scripting api m_script->initializeEnvironment(m_env); + // Do this after regular script init is done + m_script->initAsync(); + // Register us to receive map edit events servermap->addEventReceiver(this); @@ -521,7 +548,7 @@ void Server::start() actionstream << "World at [" << m_path_world << "]" << std::endl; actionstream << "Server for gameid=\"" << m_gamespec.id << "\" listening on "; - m_bind_addr.print(&actionstream); + m_bind_addr.print(actionstream); actionstream << "." << std::endl; } @@ -619,6 +646,7 @@ void Server::AsyncRunStep(bool initial_step) max_lag = dtime; } m_env->reportMaxLagEstimate(max_lag); + // Step environment m_env->step(dtime); } @@ -630,8 +658,8 @@ void Server::AsyncRunStep(bool initial_step) // Run Map's timers and unload unused data ScopeProfiler sp(g_profiler, "Server: map timer and unload"); m_env->getMap().timerUpdate(map_timer_and_unload_dtime, - g_settings->getFloat("server_unload_unused_data_timeout"), - U32_MAX); + std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f), + -1); } /* @@ -665,7 +693,7 @@ void Server::AsyncRunStep(bool initial_step) ScopeProfiler sp(g_profiler, "Server: liquid transform"); std::map<v3s16, MapBlock*> modified_blocks; - m_env->getMap().transformLiquids(modified_blocks, m_env); + m_env->getServerMap().transformLiquids(modified_blocks, m_env); /* Set the modified blocks unsent for all the clients @@ -723,28 +751,29 @@ void Server::AsyncRunStep(bool initial_step) //infostream<<"Server: Checking added and deleted active objects"<<std::endl; MutexAutoLock envlock(m_env_mutex); - m_clients.lock(); - const RemoteClientMap &clients = m_clients.getClientList(); - ScopeProfiler sp(g_profiler, "Server: update objects within range"); + { + ClientInterface::AutoLock clientlock(m_clients); + const RemoteClientMap &clients = m_clients.getClientList(); + ScopeProfiler sp(g_profiler, "Server: update objects within range"); - m_player_gauge->set(clients.size()); - for (const auto &client_it : clients) { - RemoteClient *client = client_it.second; + m_player_gauge->set(clients.size()); + for (const auto &client_it : clients) { + RemoteClient *client = client_it.second; - if (client->getState() < CS_DefinitionsSent) - continue; + if (client->getState() < CS_DefinitionsSent) + continue; - // This can happen if the client times out somehow - if (!m_env->getPlayer(client->peer_id)) - continue; + // This can happen if the client times out somehow + if (!m_env->getPlayer(client->peer_id)) + continue; - PlayerSAO *playersao = getPlayerSAO(client->peer_id); - if (!playersao) - continue; + PlayerSAO *playersao = getPlayerSAO(client->peer_id); + if (!playersao) + continue; - SendActiveObjectRemoveAdd(client, playersao); + SendActiveObjectRemoveAdd(client, playersao); + } } - m_clients.unlock(); // Write changes to the mod storage m_mod_storage_save_timer -= dtime; @@ -768,10 +797,14 @@ void Server::AsyncRunStep(bool initial_step) // Get active object messages from environment ActiveObjectMessage aom(0); - u32 aom_count = 0; + u32 count_reliable = 0, count_unreliable = 0; for(;;) { if (!m_env->getActiveObjectMessage(&aom)) break; + if (aom.reliable) + count_reliable++; + else + count_unreliable++; std::vector<ActiveObjectMessage>* message_list = nullptr; auto n = buffered_messages.find(aom.id); @@ -782,68 +815,69 @@ void Server::AsyncRunStep(bool initial_step) message_list = n->second; } message_list->push_back(std::move(aom)); - aom_count++; } - m_aom_buffer_counter->increment(aom_count); - - m_clients.lock(); - const RemoteClientMap &clients = m_clients.getClientList(); - // Route data to every client - std::string reliable_data, unreliable_data; - for (const auto &client_it : clients) { - reliable_data.clear(); - unreliable_data.clear(); - RemoteClient *client = client_it.second; - PlayerSAO *player = getPlayerSAO(client->peer_id); - // Go through all objects in message buffer - for (const auto &buffered_message : buffered_messages) { - // If object does not exist or is not known by client, skip it - u16 id = buffered_message.first; - ServerActiveObject *sao = m_env->getActiveObject(id); - if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end()) - continue; + m_aom_buffer_counter[0]->increment(count_reliable); + m_aom_buffer_counter[1]->increment(count_unreliable); - // Get message list of object - std::vector<ActiveObjectMessage>* list = buffered_message.second; - // Go through every message - for (const ActiveObjectMessage &aom : *list) { - // Send position updates to players who do not see the attachment - if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) { - if (sao->getId() == player->getId()) - continue; - - // Do not send position updates for attached players - // as long the parent is known to the client - ServerActiveObject *parent = sao->getParent(); - if (parent && client->m_known_objects.find(parent->getId()) != - client->m_known_objects.end()) - continue; - } + { + ClientInterface::AutoLock clientlock(m_clients); + const RemoteClientMap &clients = m_clients.getClientList(); + // Route data to every client + std::string reliable_data, unreliable_data; + for (const auto &client_it : clients) { + reliable_data.clear(); + unreliable_data.clear(); + RemoteClient *client = client_it.second; + PlayerSAO *player = getPlayerSAO(client->peer_id); + // Go through all objects in message buffer + for (const auto &buffered_message : buffered_messages) { + // If object does not exist or is not known by client, skip it + u16 id = buffered_message.first; + ServerActiveObject *sao = m_env->getActiveObject(id); + if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end()) + continue; - // Add full new data to appropriate buffer - std::string &buffer = aom.reliable ? reliable_data : unreliable_data; - char idbuf[2]; - writeU16((u8*) idbuf, aom.id); - // u16 id - // std::string data - buffer.append(idbuf, sizeof(idbuf)); - buffer.append(serializeString16(aom.datastring)); + // Get message list of object + std::vector<ActiveObjectMessage>* list = buffered_message.second; + // Go through every message + for (const ActiveObjectMessage &aom : *list) { + // Send position updates to players who do not see the attachment + if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) { + if (sao->getId() == player->getId()) + continue; + + // Do not send position updates for attached players + // as long the parent is known to the client + ServerActiveObject *parent = sao->getParent(); + if (parent && client->m_known_objects.find(parent->getId()) != + client->m_known_objects.end()) + continue; + } + + // Add full new data to appropriate buffer + std::string &buffer = aom.reliable ? reliable_data : unreliable_data; + char idbuf[2]; + writeU16((u8*) idbuf, aom.id); + // u16 id + // std::string data + buffer.append(idbuf, sizeof(idbuf)); + buffer.append(serializeString16(aom.datastring)); + } + } + /* + reliable_data and unreliable_data are now ready. + Send them. + */ + if (!reliable_data.empty()) { + SendActiveObjectMessages(client->peer_id, reliable_data); } - } - /* - reliable_data and unreliable_data are now ready. - Send them. - */ - if (!reliable_data.empty()) { - SendActiveObjectMessages(client->peer_id, reliable_data); - } - if (!unreliable_data.empty()) { - SendActiveObjectMessages(client->peer_id, unreliable_data, false); + if (!unreliable_data.empty()) { + SendActiveObjectMessages(client->peer_id, unreliable_data, false); + } } } - m_clients.unlock(); // Clear buffered_messages for (auto &buffered_message : buffered_messages) { @@ -858,20 +892,18 @@ void Server::AsyncRunStep(bool initial_step) // We will be accessing the environment MutexAutoLock lock(m_env_mutex); - // Don't send too many at a time - //u32 count = 0; - - // Single change sending is disabled if queue size is not small + // Single change sending is disabled if queue size is big bool disable_single_change_sending = false; if(m_unsent_map_edit_queue.size() >= 4) disable_single_change_sending = true; - int event_count = m_unsent_map_edit_queue.size(); + const auto event_count = m_unsent_map_edit_queue.size(); + m_map_edit_event_counter->increment(event_count); // We'll log the amount of each Profiler prof; - std::list<v3s16> node_meta_updates; + std::unordered_set<v3s16> node_meta_updates; while (!m_unsent_map_edit_queue.empty()) { MapEditEvent* event = m_unsent_map_edit_queue.front(); @@ -898,9 +930,7 @@ void Server::AsyncRunStep(bool initial_step) case MEET_BLOCK_NODE_METADATA_CHANGED: { prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1); if (!event->is_private_change) { - // Don't send the change yet. Collect them to eliminate dupes. - node_meta_updates.remove(event->p); - node_meta_updates.push_back(event->p); + node_meta_updates.emplace(event->p); } if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx( @@ -953,7 +983,7 @@ void Server::AsyncRunStep(bool initial_step) } // Send all metadata updates - if (node_meta_updates.size()) + if (!node_meta_updates.empty()) sendMetadataChanged(node_meta_updates); } @@ -1036,8 +1066,7 @@ void Server::Receive() } catch (const ClientStateError &e) { errorstream << "ProcessData: peer=" << peer_id << " what()=" << e.what() << std::endl; - DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect." - L"Try reconnecting or updating your client"); + DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); } catch (const con::PeerNotFoundException &e) { // Do nothing } catch (const con::NoIncomingDataException &e) { @@ -1050,18 +1079,14 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) { std::string playername; PlayerSAO *playersao = NULL; - m_clients.lock(); - try { + { + ClientInterface::AutoLock clientlock(m_clients); RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone); if (client) { playername = client->getName(); playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version); } - } catch (std::exception &e) { - m_clients.unlock(); - throw; } - m_clients.unlock(); RemotePlayer *player = m_env->getPlayer(playername.c_str()); @@ -1070,15 +1095,13 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) if (player && player->getPeerId() != PEER_ID_INEXISTENT) { actionstream << "Server: Failed to emerge player \"" << playername << "\" (player allocated to an another client)" << std::endl; - DenyAccess_Legacy(peer_id, L"Another client is connected with this " - L"name. If your client closed unexpectedly, try again in " - L"a minute."); + DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED); } else { errorstream << "Server: " << playername << ": Failed to emerge player" << std::endl; - DenyAccess_Legacy(peer_id, L"Could not allocate player."); + DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); } - return NULL; + return nullptr; } /* @@ -1096,7 +1119,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) SendInventory(playersao, false); // Send HP - SendPlayerHP(playersao); + SendPlayerHP(playersao, false); // Send death screen if (playersao->isDead()) @@ -1143,18 +1166,16 @@ void Server::ProcessData(NetworkPacket *pkt) Address address = getPeerAddress(peer_id); std::string addr_s = address.serializeString(); - if(m_banmanager->isIpBanned(addr_s)) { + // FIXME: Isn't it a bit excessive to check this for every packet? + if (m_banmanager->isIpBanned(addr_s)) { std::string ban_name = m_banmanager->getBanName(addr_s); infostream << "Server: A banned client tried to connect from " - << addr_s << "; banned name was " - << ban_name << std::endl; - // This actually doesn't seem to transfer to the client - DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was " - + utf8_to_wide(ban_name)); + << addr_s << "; banned name was " << ban_name << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, + "Your IP is banned. Banned name was " + ban_name); return; } - } - catch(con::PeerNotFoundException &e) { + } catch (con::PeerNotFoundException &e) { /* * no peer for this packet found * most common reason is peer timeout, e.g. peer didn't @@ -1234,13 +1255,12 @@ void Server::onMapEditEvent(const MapEditEvent &event) void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block) { std::vector<session_t> clients = m_clients.getClientIDs(); - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); // Set the modified blocks unsent for all the clients for (const session_t client_id : clients) { if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id)) client->SetBlocksNotSent(block); } - m_clients.unlock(); } void Server::peerAdded(con::Peer *peer) @@ -1268,13 +1288,11 @@ bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* bool Server::getClientInfo(session_t peer_id, ClientInfo &ret) { - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid); - if (!client) { - m_clients.unlock(); + if (!client) return false; - } ret.state = client->getState(); ret.addr = client->getAddress(); @@ -1289,8 +1307,6 @@ bool Server::getClientInfo(session_t peer_id, ClientInfo &ret) ret.lang_code = client->getLangCode(); - m_clients.unlock(); - return true; } @@ -1370,7 +1386,7 @@ void Server::SendMovement(session_t peer_id) void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason) { m_script->player_event(playersao, "health_changed"); - SendPlayerHP(playersao); + SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX); // Send to other clients playersao->sendPunchCommand(); @@ -1379,15 +1395,15 @@ void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReas HandlePlayerDeath(playersao, reason); } -void Server::SendPlayerHP(PlayerSAO *playersao) +void Server::SendPlayerHP(PlayerSAO *playersao, bool effect) { - SendHP(playersao->getPeerID(), playersao->getHP()); + SendHP(playersao->getPeerID(), playersao->getHP(), effect); } -void Server::SendHP(session_t peer_id, u16 hp) +void Server::SendHP(session_t peer_id, u16 hp, bool effect) { - NetworkPacket pkt(TOCLIENT_HP, 1, peer_id); - pkt << hp; + NetworkPacket pkt(TOCLIENT_HP, 3, peer_id); + pkt << hp << effect; Send(&pkt); } @@ -1413,13 +1429,6 @@ void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason, Send(&pkt); } -void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason) -{ - NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id); - pkt << reason; - Send(&pkt); -} - void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target, v3f camera_point_target) { @@ -1596,7 +1605,12 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, if (peer_id == PEER_ID_INEXISTENT) { std::vector<session_t> clients = m_clients.getClientIDs(); - const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS; + const v3f pos = ( + p.pos.start.min.val + + p.pos.start.max.val + + p.pos.end.min.val + + p.pos.end.max.val + ) / 4.0f * BS; const float radius_sq = radius * radius; /* Don't send short-lived spawners to distant players. * This could be replaced with proper tracking at some point. */ @@ -1624,11 +1638,19 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); - pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel - << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime - << p.minsize << p.maxsize << p.collisiondetection; + pkt << p.amount << p.time; + { // serialize legacy fields + std::ostringstream os(std::ios_base::binary); + p.pos.start.legacySerialize(os); + p.vel.start.legacySerialize(os); + p.acc.start.legacySerialize(os); + p.exptime.start.legacySerialize(os); + p.size.start.legacySerialize(os); + pkt.putRawString(os.str()); + } + pkt << p.collisiondetection; - pkt.putLongString(p.texture); + pkt.putLongString(p.texture.string); pkt << id << p.vertical << p.collision_removal << attached_id; { @@ -1639,6 +1661,51 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, pkt << p.glow << p.object_collision; pkt << p.node.param0 << p.node.param2 << p.node_tile; + { // serialize new fields + // initial bias for older properties + pkt << p.pos.start.bias + << p.vel.start.bias + << p.acc.start.bias + << p.exptime.start.bias + << p.size.start.bias; + + std::ostringstream os(std::ios_base::binary); + + // final tween frames of older properties + p.pos.end.serialize(os); + p.vel.end.serialize(os); + p.acc.end.serialize(os); + p.exptime.end.serialize(os); + p.size.end.serialize(os); + + // properties for legacy texture field + p.texture.serialize(os, protocol_version, true); + + // new properties + p.drag.serialize(os); + p.jitter.serialize(os); + p.bounce.serialize(os); + ParticleParamTypes::serializeParameterValue(os, p.attractor_kind); + if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) { + p.attract.serialize(os); + p.attractor_origin.serialize(os); + writeU16(os, p.attractor_attachment); /* object ID */ + writeU8(os, p.attractor_kill); + if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) { + p.attractor_direction.serialize(os); + writeU16(os, p.attractor_direction_attachment); + } + } + p.radius.serialize(os); + + ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size()); + for (const auto& tex : p.texpool) { + tex.serialize(os, protocol_version); + } + + pkt.putRawString(os.str()); + } + Send(&pkt); } @@ -1778,7 +1845,8 @@ void Server::SendSetStars(session_t peer_id, const StarParams ¶ms) NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id); pkt << params.visible << params.count - << params.starcolor << params.scale; + << params.starcolor << params.scale + << params.day_opacity; Send(&pkt); } @@ -1802,6 +1870,16 @@ void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override, Send(&pkt); } +void Server::SendSetLighting(session_t peer_id, const Lighting &lighting) +{ + NetworkPacket pkt(TOCLIENT_SET_LIGHTING, + 4, peer_id); + + pkt << lighting.shadow_intensity; + + Send(&pkt); +} + void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed) { NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id); @@ -2059,14 +2137,13 @@ inline s32 Server::nextSoundId() return ret; } -s32 Server::playSound(const SimpleSoundSpec &spec, - const ServerSoundParams ¶ms, bool ephemeral) +s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral) { // Find out initial position of sound bool pos_exists = false; - v3f pos = params.getPos(m_env, &pos_exists); + const v3f pos = params.getPos(m_env, &pos_exists); // If position is not found while it should be, cancel sound - if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL)) + if(pos_exists != (params.type != SoundLocation::Local)) return -1; // Filter destination clients @@ -2111,101 +2188,68 @@ s32 Server::playSound(const SimpleSoundSpec &spec, if(dst_clients.empty()) return -1; - // Create the sound - s32 id; - ServerPlayingSound *psound = nullptr; - if (ephemeral) { - id = -1; // old clients will still use this, so pick a reserved ID - } else { - id = nextSoundId(); - // The sound will exist as a reference in m_playing_sounds - m_playing_sounds[id] = ServerPlayingSound(); - psound = &m_playing_sounds[id]; - psound->params = params; - psound->spec = spec; - } + // old clients will still use this, so pick a reserved ID (-1) + const s32 id = ephemeral ? -1 : nextSoundId(); - float gain = params.gain * spec.gain; + float gain = params.gain * params.spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); - pkt << id << spec.name << gain + pkt << id << params.spec.name << gain << (u8) params.type << pos << params.object - << params.loop << params.fade << params.pitch + << params.spec.loop << params.spec.fade << params.spec.pitch << ephemeral; bool as_reliable = !ephemeral; - for (const u16 dst_client : dst_clients) { - if (psound) - psound->clients.insert(dst_client); - m_clients.send(dst_client, 0, &pkt, as_reliable); + for (const session_t peer_id : dst_clients) { + if (!ephemeral) + params.clients.insert(peer_id); + m_clients.send(peer_id, 0, &pkt, as_reliable); } + + if (!ephemeral) + m_playing_sounds[id] = std::move(params); return id; } void Server::stopSound(s32 handle) { - // Get sound reference - std::unordered_map<s32, ServerPlayingSound>::iterator i = - m_playing_sounds.find(handle); - if (i == m_playing_sounds.end()) + auto it = m_playing_sounds.find(handle); + if (it == m_playing_sounds.end()) return; - ServerPlayingSound &psound = i->second; + + ServerPlayingSound &psound = it->second; NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4); pkt << handle; - for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin(); - si != psound.clients.end(); ++si) { + for (session_t peer_id : psound.clients) { // Send as reliable - m_clients.send(*si, 0, &pkt, true); + m_clients.send(peer_id, 0, &pkt, true); } + // Remove sound reference - m_playing_sounds.erase(i); + m_playing_sounds.erase(it); } void Server::fadeSound(s32 handle, float step, float gain) { - // Get sound reference - std::unordered_map<s32, ServerPlayingSound>::iterator i = - m_playing_sounds.find(handle); - if (i == m_playing_sounds.end()) + auto it = m_playing_sounds.find(handle); + if (it == m_playing_sounds.end()) return; - ServerPlayingSound &psound = i->second; - psound.params.gain = gain; + ServerPlayingSound &psound = it->second; + psound.gain = gain; // destination gain NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4); pkt << handle << step << gain; - // Backwards compability - bool play_sound = gain > 0; - ServerPlayingSound compat_psound = psound; - compat_psound.clients.clear(); - - NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4); - compat_pkt << handle; - - for (std::unordered_set<u16>::iterator it = psound.clients.begin(); - it != psound.clients.end();) { - if (m_clients.getProtocolVersion(*it) >= 32) { - // Send as reliable - m_clients.send(*it, 0, &pkt, true); - ++it; - } else { - compat_psound.clients.insert(*it); - // Stop old sound - m_clients.send(*it, 0, &compat_pkt, true); - psound.clients.erase(it++); - } + for (session_t peer_id : psound.clients) { + // Send as reliable + m_clients.send(peer_id, 0, &pkt, true); } // Remove sound reference - if (!play_sound || psound.clients.empty()) - m_playing_sounds.erase(i); - - if (play_sound && !compat_psound.clients.empty()) { - // Play new sound volume on older clients - playSound(compat_psound.spec, compat_psound.params); - } + if (gain <= 0 || psound.clients.empty()) + m_playing_sounds.erase(it); } void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players, @@ -2219,7 +2263,7 @@ void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players, pkt << p; std::vector<session_t> clients = m_clients.getClientIDs(); - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); for (session_t client_id : clients) { RemoteClient *client = m_clients.lockedGetClientNoEx(client_id); @@ -2242,8 +2286,6 @@ void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players, // Send as reliable m_clients.send(client_id, 0, &pkt, true); } - - m_clients.unlock(); } void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players, @@ -2258,7 +2300,7 @@ void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_player << (u8) (remove_metadata ? 0 : 1); std::vector<session_t> clients = m_clients.getClientIDs(); - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); for (session_t client_id : clients) { RemoteClient *client = m_clients.lockedGetClientNoEx(client_id); @@ -2281,35 +2323,35 @@ void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_player // Send as reliable m_clients.send(client_id, 0, &pkt, true); } - - m_clients.unlock(); } -void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes) +void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes) { - float maxd = far_d_nodes * BS; NodeMetadataList meta_updates_list(false); - std::vector<session_t> clients = m_clients.getClientIDs(); + std::ostringstream os(std::ios::binary); - m_clients.lock(); + std::vector<session_t> clients = m_clients.getClientIDs(); + ClientInterface::AutoLock clientlock(m_clients); for (session_t i : clients) { RemoteClient *client = m_clients.lockedGetClientNoEx(i); if (!client) continue; - ServerActiveObject *player = m_env->getActiveObject(i); - v3f player_pos = player ? player->getBasePosition() : v3f(); + ServerActiveObject *player = getPlayerSAO(i); + v3s16 player_pos; + if (player) + player_pos = floatToInt(player->getBasePosition(), BS); - for (const v3s16 &pos : meta_updates) { + for (const v3s16 pos : positions) { NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos); if (!meta) continue; v3s16 block_pos = getNodeBlockPos(pos); - if (!client->isBlockSent(block_pos) || (player && - player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) { + if (!client->isBlockSent(block_pos) || + player_pos.getDistanceFrom(pos) > far_d_nodes) { client->SetBlockNotSent(block_pos); continue; } @@ -2321,38 +2363,49 @@ void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far continue; // Send the meta changes - std::ostringstream os(std::ios::binary); + os.str(""); meta_updates_list.serialize(os, client->serialization_version, false, true, true); - std::ostringstream oss(std::ios::binary); - compressZlib(os.str(), oss); + std::string raw = os.str(); + os.str(""); + compressZlib(raw, os); - NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0); - pkt.putLongString(oss.str()); - m_clients.send(i, 0, &pkt, true); + NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i); + pkt.putLongString(os.str()); + Send(&pkt); meta_updates_list.clear(); } - - m_clients.unlock(); } void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, - u16 net_proto_version) + u16 net_proto_version, SerializedBlockCache *cache) { - /* - Create a packet with the block in the right format - */ thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9); - std::ostringstream os(std::ios_base::binary); - block->serialize(os, ver, false, net_compression_level); - block->serializeNetworkSpecific(os); - std::string s = os.str(); + std::string s, *sptr = nullptr; - NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id); + if (cache) { + auto it = cache->find({block->getPos(), ver}); + if (it != cache->end()) + sptr = &it->second; + } + // Serialize the block in the right format + if (!sptr) { + std::ostringstream os(std::ios_base::binary); + block->serialize(os, ver, false, net_compression_level); + block->serializeNetworkSpecific(os); + s = os.str(); + sptr = &s; + } + + NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id); pkt << block->getPos(); - pkt.putRawString(s.c_str(), s.size()); + pkt.putRawString(*sptr); Send(&pkt); + + // Store away in cache + if (cache && sptr == &s) + (*cache)[{block->getPos(), ver}] = std::move(s); } void Server::SendBlocks(float dtime) @@ -2362,14 +2415,14 @@ void Server::SendBlocks(float dtime) std::vector<PrioritySortedBlockTransfer> queue; - u32 total_sending = 0; + u32 total_sending = 0, unique_clients = 0; { ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list"); std::vector<session_t> clients = m_clients.getClientIDs(); - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); for (const session_t client_id : clients) { RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active); @@ -2377,9 +2430,10 @@ void Server::SendBlocks(float dtime) continue; total_sending += client->getSendingCount(); + const auto old_count = queue.size(); client->GetNextBlocks(m_env,m_emerge, dtime, queue); + unique_clients += queue.size() > old_count ? 1 : 0; } - m_clients.unlock(); } // Sort. @@ -2387,7 +2441,7 @@ void Server::SendBlocks(float dtime) // Lowest is most important. std::sort(queue.begin(), queue.end()); - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); // Maximal total count calculation // The per-client block sends is halved with the maximal online users @@ -2397,6 +2451,12 @@ void Server::SendBlocks(float dtime) ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients"); Map &map = m_env->getMap(); + SerializedBlockCache cache, *cache_ptr = nullptr; + if (unique_clients > 1) { + // caching is pointless with a single client + cache_ptr = &cache; + } + for (const PrioritySortedBlockTransfer &block_to_send : queue) { if (total_sending >= max_blocks_to_send) break; @@ -2411,12 +2471,11 @@ void Server::SendBlocks(float dtime) continue; SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version, - client->net_proto_version); + client->net_proto_version, cache_ptr); client->SentBlock(block_to_send.pos); total_sending++; } - m_clients.unlock(); } bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos) @@ -2425,15 +2484,12 @@ bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos) if (!block) return false; - m_clients.lock(); + ClientInterface::AutoLock clientlock(m_clients); RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active); - if (!client || client->isBlockSent(blockpos)) { - m_clients.unlock(); + if (!client || client->isBlockSent(blockpos)) return false; - } SendBlockNoLock(peer_id, block, client->serialization_version, client->net_proto_version); - m_clients.unlock(); return true; } @@ -2766,9 +2822,10 @@ void Server::RespawnPlayer(session_t peer_id) << playersao->getPlayer()->getName() << " respawns" << std::endl; - playersao->setHP(playersao->accessObjectProperties()->hp_max, + const auto *prop = playersao->accessObjectProperties(); + playersao->setHP(prop->hp_max, PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN)); - playersao->setBreath(playersao->accessObjectProperties()->breath_max); + playersao->setBreath(prop->breath_max); bool repositioned = m_script->on_respawnplayer(playersao); if (!repositioned) { @@ -2785,29 +2842,10 @@ void Server::DenySudoAccess(session_t peer_id) } -void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason, - const std::string &str_reason, bool reconnect) -{ - SendAccessDenied(peer_id, reason, str_reason, reconnect); - - m_clients.event(peer_id, CSE_SetDenied); - DisconnectPeer(peer_id); -} - - void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason, - const std::string &custom_reason) -{ - SendAccessDenied(peer_id, reason, custom_reason); - m_clients.event(peer_id, CSE_SetDenied); - DisconnectPeer(peer_id); -} - -// 13/03/15: remove this function when protocol version 25 will become -// the minimum version for MT users, maybe in 1 year -void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason) + const std::string &custom_reason, bool reconnect) { - SendAccessDenied_Legacy(peer_id, reason); + SendAccessDenied(peer_id, reason, custom_reason, reconnect); m_clients.event(peer_id, CSE_SetDenied); DisconnectPeer(peer_id); } @@ -2993,8 +3031,8 @@ std::wstring Server::handleChat(const std::string &name, return ws.str(); } case RPLAYER_CHATRESULT_KICK: - DenyAccess_Legacy(player->getPeerId(), - L"You have been kicked due to message flooding."); + DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING, + "You have been kicked due to message flooding."); return L""; case RPLAYER_CHATRESULT_OK: break; @@ -3111,7 +3149,7 @@ std::string Server::getStatusString() // Version os << "version: " << g_version_string; // Game - os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name); + os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title); // Uptime os << " | uptime: " << duration_to_string((int) m_uptime_counter->get()); // Max lag estimate @@ -3290,9 +3328,12 @@ bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask) if (!player) return false; + u32 new_hud_flags = (player->hud_flags & ~mask) | flags; + if (new_hud_flags == player->hud_flags) // no change + return true; + SendHUDSetFlags(player->getPeerId(), flags, mask); - player->hud_flags &= ~mask; - player->hud_flags |= flags; + player->hud_flags = new_hud_flags; PlayerSAO* playersao = player->getPlayerSAO(); @@ -3401,6 +3442,13 @@ void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override, SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio); } +void Server::setLighting(RemotePlayer *player, const Lighting &lighting) +{ + sanity_check(player); + player->setLighting(lighting); + SendSetLighting(player->getPeerId(), lighting); +} + void Server::notifyPlayers(const std::wstring &msg) { SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg)); @@ -3535,48 +3583,49 @@ bool Server::dynamicAddMedia(std::string filepath, legacy_pkt.putLongString(filedata); std::unordered_set<session_t> delivered, waiting; - m_clients.lock(); - for (auto &pair : m_clients.getClientList()) { - if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) { - /* - If a client is in the DefinitionsSent state it is too late to - transfer the file via sendMediaAnnouncement() but at the same - time the client cannot accept a media push yet. - Short of artificially delaying the joining process there is no - way for the server to resolve this so we (currently) opt not to. - */ - warningstream << "The media \"" << filename << "\" (dynamic) could " - "not be delivered to " << pair.second->getName() - << " due to a race condition." << std::endl; - continue; - } - if (pair.second->getState() < CS_Active) - continue; + { + ClientInterface::AutoLock clientlock(m_clients); + for (auto &pair : m_clients.getClientList()) { + if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) { + /* + If a client is in the DefinitionsSent state it is too late to + transfer the file via sendMediaAnnouncement() but at the same + time the client cannot accept a media push yet. + Short of artificially delaying the joining process there is no + way for the server to resolve this so we (currently) opt not to. + */ + warningstream << "The media \"" << filename << "\" (dynamic) could " + "not be delivered to " << pair.second->getName() + << " due to a race condition." << std::endl; + continue; + } + if (pair.second->getState() < CS_Active) + continue; - const auto proto_ver = pair.second->net_proto_version; - if (proto_ver < 39) - continue; + const auto proto_ver = pair.second->net_proto_version; + if (proto_ver < 39) + continue; - const session_t peer_id = pair.second->peer_id; - if (!to_player.empty() && getPlayerName(peer_id) != to_player) - continue; + const session_t peer_id = pair.second->peer_id; + if (!to_player.empty() && getPlayerName(peer_id) != to_player) + continue; - if (proto_ver < 40) { - delivered.emplace(peer_id); - /* - The network layer only guarantees ordered delivery inside a channel. - Since the very next packet could be one that uses the media, we have - to push the media over ALL channels to ensure it is processed before - it is used. In practice this means channels 1 and 0. - */ - m_clients.send(peer_id, 1, &legacy_pkt, true); - m_clients.send(peer_id, 0, &legacy_pkt, true); - } else { - waiting.emplace(peer_id); - Send(peer_id, &pkt); + if (proto_ver < 40) { + delivered.emplace(peer_id); + /* + The network layer only guarantees ordered delivery inside a channel. + Since the very next packet could be one that uses the media, we have + to push the media over ALL channels to ensure it is processed before + it is used. In practice this means channels 1 and 0. + */ + m_clients.send(peer_id, 1, &legacy_pkt, true); + m_clients.send(peer_id, 0, &legacy_pkt, true); + } else { + waiting.emplace(peer_id); + Send(peer_id, &pkt); + } } } - m_clients.unlock(); // Run callback for players that already had the file delivered (legacy-only) for (session_t peer_id : delivered) { @@ -3686,11 +3735,6 @@ const ModSpec *Server::getModSpec(const std::string &modname) const return m_modmgr->getModSpec(modname); } -void Server::getModNames(std::vector<std::string> &modlist) -{ - m_modmgr->getModNames(modlist); -} - std::string Server::getBuiltinLuaPath() { return porting::path_share + DIR_DELIM + "builtin"; @@ -3712,8 +3756,8 @@ v3f Server::findSpawnPos() s32 range = MYMIN(1 + i, range_max); // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( - -range + (myrand() % (range * 2)), - -range + (myrand() % (range * 2))); + -range + myrand_range(0, range*2), + -range + myrand_range(0, range*2)); // Get spawn level at point s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d); // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to |