]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/server.cpp
Update German locale translation
[dragonfireclient.git] / src / server.cpp
index 81cdd1f8d1ea6224d93cf66c5c944fe4efe58a1a..c175cbcd29f3cbf9eec7e6b411c0a9e64b5943dc 100644 (file)
@@ -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);
                }
        }
 
@@ -512,9 +517,7 @@ void Server::stop()
 
        // Stop threads (set run=false first so both start stopping)
        m_thread->stop();
-       //m_emergethread.setRun(false);
        m_thread->wait();
-       //m_emergethread.stop();
 
        infostream<<"Server: Threads stopped"<<std::endl;
 }
@@ -665,6 +668,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
        {
@@ -938,14 +952,14 @@ void Server::AsyncRunStep(bool initial_step)
        }
 
        /*
-               Trigger emergethread (it somehow gets to a non-triggered but
-               bysy state sometimes)
+               Trigger emerge thread
+               Doing this every 2s is left over from old code, unclear if this is still needed.
        */
        {
                float &counter = m_emergethread_trigger_timer;
-               counter += dtime;
-               if (counter >= 2.0) {
-                       counter = 0.0;
+               counter -= dtime;
+               if (counter <= 0.0f) {
+                       counter = 2.0f;
 
                        m_emerge->startThreads();
                }
@@ -1079,8 +1093,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 +1651,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 +1686,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 +2323,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 +2452,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
@@ -2496,7 +2505,9 @@ void Server::fillMediaCache()
 
        // Collect all media file paths
        std::vector<std::string> paths;
-       // The paths are ordered in descending priority
+
+       // ordered in descending priority
+       paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
        fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
        fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
        m_modmgr->getModsMediaPaths(paths);
@@ -2530,6 +2541,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++;
@@ -2538,6 +2551,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;
@@ -2556,11 +2571,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))
        {}
 };
 
@@ -2587,40 +2600,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;
-                       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;
+               std::string data;
+               if (!fs::ReadFile(m.path, data)) {
+                       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) {
@@ -2663,6 +2655,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)
 {
@@ -2999,6 +3018,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";
        }
@@ -3095,15 +3117,16 @@ std::string Server::getStatusString()
        std::ostringstream os(std::ios_base::binary);
        os << "# Server: ";
        // Version
-       os << "version=" << g_version_string;
+       os << "version" << g_version_string;
        // Uptime
-       os << ", uptime=" << m_uptime_counter->get();
+       os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
        // Max lag estimate
-       os << ", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0);
+       os << " | max lag: " << std::setprecision(3);
+       os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
 
        // Information about clients
        bool first = true;
-       os << ", clients={";
+       os << " | clients: ";
        if (m_env) {
                std::vector<session_t> clients = m_clients.getClientIDs();
                for (session_t client_id : clients) {
@@ -3120,7 +3143,6 @@ std::string Server::getStatusString()
                        os << name;
                }
        }
-       os << "}";
 
        if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
                os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
@@ -3457,14 +3479,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
@@ -3473,35 +3499,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;
 }