diff options
Diffstat (limited to 'src/server.cpp')
-rw-r--r-- | src/server.cpp | 222 |
1 files changed, 152 insertions, 70 deletions
diff --git a/src/server.cpp b/src/server.cpp index a8d452783..7fb9a78e9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -103,7 +103,13 @@ void *ServerThread::run() * doesn't busy wait) and will process any remaining packets. */ - m_server->AsyncRunStep(true); + try { + m_server->AsyncRunStep(true); + } catch (con::ConnectionBindFailed &e) { + m_server->setAsyncFatalError(e.what()); + } catch (LuaError &e) { + m_server->setAsyncFatalError(e); + } while (!stopRequested()) { try { @@ -117,8 +123,7 @@ void *ServerThread::run() } catch (con::ConnectionBindFailed &e) { m_server->setAsyncFatalError(e.what()); } catch (LuaError &e) { - m_server->setAsyncFatalError( - "ServerThread::run Lua: " + std::string(e.what())); + m_server->setAsyncFatalError(e); } } @@ -665,6 +670,17 @@ void Server::AsyncRunStep(bool initial_step) } else { m_lag_gauge->increment(dtime/100); } + + { + float &counter = m_step_pending_dyn_media_timer; + counter += dtime; + if (counter >= 5.0f) { + stepPendingDynMediaCallbacks(counter); + counter = 0; + } + } + + #if USE_CURL // send masterserver announce { @@ -1079,8 +1095,7 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) if (playersao->isDead()) SendDeathscreen(peer_id, false, v3f(0,0,0)); else - SendPlayerHPOrDie(playersao, - PlayerHPChangeReason(PlayerHPChangeReason::SET_HP)); + SendPlayerHP(peer_id); // Send Breath SendPlayerBreath(playersao); @@ -1638,7 +1653,7 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir << form->align << form->offset << form->world_pos << form->size - << form->z_index << form->text2; + << form->z_index << form->text2 << form->style; Send(&pkt); } @@ -1673,10 +1688,7 @@ void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void case HUD_STAT_SIZE: pkt << *(v2s32 *) value; break; - case HUD_STAT_NUMBER: - case HUD_STAT_ITEM: - case HUD_STAT_DIR: - default: + default: // all other types pkt << *(u32 *) value; break; } @@ -2313,7 +2325,7 @@ void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far // Send the meta changes std::ostringstream os(std::ios::binary); - meta_updates_list.serialize(os, client->net_proto_version, false, true); + meta_updates_list.serialize(os, client->serialization_version, false, true, true); std::ostringstream oss(std::ios::binary); compressZlib(os.str(), oss); @@ -2442,9 +2454,8 @@ bool Server::addMediaFile(const std::string &filename, // If name is not in a supported format, ignore it const char *supported_ext[] = { ".png", ".jpg", ".bmp", ".tga", - ".pcx", ".ppm", ".psd", ".wal", ".rgb", ".ogg", - ".x", ".b3d", ".md2", ".obj", + ".x", ".b3d", ".obj", // Custom translation file format ".tr", NULL @@ -2532,6 +2543,8 @@ void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_co std::string lang_suffix; lang_suffix.append(".").append(lang_code).append(".tr"); for (const auto &i : m_media) { + if (i.second.no_announce) + continue; if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix)) continue; media_sent++; @@ -2540,6 +2553,8 @@ void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_co pkt << media_sent; for (const auto &i : m_media) { + if (i.second.no_announce) + continue; if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix)) continue; pkt << i.first << i.second.sha1_digest; @@ -2558,11 +2573,9 @@ struct SendableMedia std::string path; std::string data; - SendableMedia(const std::string &name_="", const std::string &path_="", - const std::string &data_=""): - name(name_), - path(path_), - data(data_) + SendableMedia(const std::string &name, const std::string &path, + std::string &&data): + name(name), path(path), data(std::move(data)) {} }; @@ -2589,40 +2602,19 @@ void Server::sendRequestedMedia(session_t peer_id, continue; } - //TODO get path + name - std::string tpath = m_media[name].path; + const auto &m = m_media[name]; // Read data - std::ifstream fis(tpath.c_str(), std::ios_base::binary); - if(!fis.good()){ - errorstream<<"Server::sendRequestedMedia(): Could not open \"" - <<tpath<<"\" for reading"<<std::endl; + std::string data; + if (!fs::ReadFile(m.path, data)) { + errorstream << "Server::sendRequestedMedia(): Failed to read \"" + << name << "\"" << std::endl; continue; } - std::ostringstream tmp_os(std::ios_base::binary); - bool bad = false; - for(;;) { - char buf[1024]; - fis.read(buf, 1024); - std::streamsize len = fis.gcount(); - tmp_os.write(buf, len); - file_size_bunch_total += len; - if(fis.eof()) - break; - if(!fis.good()) { - bad = true; - break; - } - } - if (bad) { - errorstream<<"Server::sendRequestedMedia(): Failed to read \"" - <<name<<"\""<<std::endl; - continue; - } - /*infostream<<"Server::sendRequestedMedia(): Loaded \"" - <<tname<<"\""<<std::endl;*/ + file_size_bunch_total += data.size(); + // Put in list - file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str()); + file_bunches.back().emplace_back(name, m.path, std::move(data)); // Start next bunch if got enough data if(file_size_bunch_total >= bytes_per_bunch) { @@ -2665,6 +2657,33 @@ void Server::sendRequestedMedia(session_t peer_id, } } +void Server::stepPendingDynMediaCallbacks(float dtime) +{ + MutexAutoLock lock(m_env_mutex); + + for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) { + it->second.expiry_timer -= dtime; + bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0; + + if (!del) { + it++; + continue; + } + + const auto &name = it->second.filename; + if (!name.empty()) { + assert(m_media.count(name)); + // if no_announce isn't set we're definitely deleting the wrong file! + sanity_check(m_media[name].no_announce); + + fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path); + m_media.erase(name); + } + getScriptIface()->freeDynamicMediaCallback(it->first); + it = m_pending_dyn_media.erase(it); + } +} + void Server::SendMinimapModes(session_t peer_id, std::vector<MinimapMode> &modes, size_t wanted_mode) { @@ -3001,6 +3020,9 @@ std::wstring Server::handleChat(const std::string &name, } auto message = trim(wide_to_utf8(wmessage)); + if (message.empty()) + return L""; + if (message.find_first_of("\n\r") != std::wstring::npos) { return L"Newlines are not permitted in chat messages"; } @@ -3459,14 +3481,18 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id) SendDeleteParticleSpawner(peer_id, id); } -bool Server::dynamicAddMedia(const std::string &filepath, - std::vector<RemotePlayer*> &sent_to) +bool Server::dynamicAddMedia(std::string filepath, + const u32 token, const std::string &to_player, bool ephemeral) { std::string filename = fs::GetFilenameFromPath(filepath.c_str()); - if (m_media.find(filename) != m_media.end()) { - errorstream << "Server::dynamicAddMedia(): file \"" << filename - << "\" already exists in media cache" << std::endl; - return false; + auto it = m_media.find(filename); + if (it != m_media.end()) { + // Allow the same path to be "added" again in certain conditions + if (ephemeral || it->second.path != filepath) { + errorstream << "Server::dynamicAddMedia(): file \"" << filename + << "\" already exists in media cache" << std::endl; + return false; + } } // Load the file and add it to our media cache @@ -3475,35 +3501,91 @@ bool Server::dynamicAddMedia(const std::string &filepath, if (!ok) return false; + if (ephemeral) { + // Create a copy of the file and swap out the path, this removes the + // requirement that mods keep the file accessible at the original path. + filepath = fs::CreateTempFile(); + bool ok = ([&] () -> bool { + if (filepath.empty()) + return false; + std::ofstream os(filepath.c_str(), std::ios::binary); + if (!os.good()) + return false; + os << filedata; + os.close(); + return !os.fail(); + })(); + if (!ok) { + errorstream << "Server: failed to create a copy of media file " + << "\"" << filename << "\"" << std::endl; + m_media.erase(filename); + return false; + } + verbosestream << "Server: \"" << filename << "\" temporarily copied to " + << filepath << std::endl; + + m_media[filename].path = filepath; + m_media[filename].no_announce = true; + // stepPendingDynMediaCallbacks will clean this up later. + } else if (!to_player.empty()) { + m_media[filename].no_announce = true; + } + // Push file to existing clients NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0); - pkt << raw_hash << filename << (bool) true; - pkt.putLongString(filedata); + pkt << raw_hash << filename << (bool)ephemeral; + NetworkPacket legacy_pkt = pkt; + + // Newer clients get asked to fetch the file (asynchronous) + pkt << token; + // Older clients have an awful hack that just throws the data at them + 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) continue; - if (pair.second->net_proto_version < 39) + const auto proto_ver = pair.second->net_proto_version; + if (proto_ver < 39) continue; - if (auto player = m_env->getPlayer(pair.second->peer_id)) - sent_to.emplace_back(player); - /* - FIXME: this is a very awful hack - 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 we have to send it twice: - - channel 1 (HUD) - - channel 0 (everything else: e.g. play_sound, object messages) - */ - m_clients.send(pair.second->peer_id, 1, &pkt, true); - m_clients.send(pair.second->peer_id, 0, &pkt, true); + 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); + } } m_clients.unlock(); + // Run callback for players that already had the file delivered (legacy-only) + for (session_t peer_id : delivered) { + if (auto player = m_env->getPlayer(peer_id)) + getScriptIface()->on_dynamic_media_added(token, player->getName()); + } + + // Save all others in our pending state + auto &state = m_pending_dyn_media[token]; + state.waiting_players = std::move(waiting); + // regardless of success throw away the callback after a while + state.expiry_timer = 60.0f; + if (ephemeral) + state.filename = filename; + return true; } |