diff --git a/src/client/client.h b/src/client/client.h index 3c7fc0db2..44a0de719 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -552,8 +552,6 @@ private: std::vector m_remote_media_servers; // Media downloader, only exists during init ClientMediaDownloader *m_media_downloader; - // Set of media filenames pushed by server at runtime - std::unordered_set m_media_pushed_files; // Pending downloads of dynamic media (key: token) std::vector>> m_pending_media_downloads; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index d36459bb2..35e5585a0 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -171,6 +171,7 @@ struct TextureInfo { std::string name; video::ITexture *texture; + std::set sourceImages; TextureInfo( const std::string &name_, @@ -180,6 +181,17 @@ struct TextureInfo texture(texture_) { } + + TextureInfo( + const std::string &name_, + video::ITexture *texture_, + std::set &sourceImages_ + ): + name(name_), + texture(texture_), + sourceImages(sourceImages_) + { + } }; /* @@ -379,19 +391,26 @@ private: // This should be only accessed from the main thread SourceImageCache m_sourcecache; + // Rebuild images and textures from the current set of source images + // Shall be called from the main thread. + // You ARE expected to be holding m_textureinfo_cache_mutex + void rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti); + // Generate a texture u32 generateTexture(const std::string &name); // Generate image based on a string like "stone.png" or "[crack:1:0". // if baseimg is NULL, it is created. Otherwise stuff is made on it. - bool generateImagePart(std::string part_of_name, video::IImage *& baseimg); + // source_image_names is important to determine when to flush the image from a cache (dynamic media) + bool generateImagePart(std::string part_of_name, video::IImage *& baseimg, std::set &source_image_names); /*! Generates an image from a full string like * "stone.png^mineral_coal.png^[crack:1:0". * Shall be called from the main thread. * The returned Image should be dropped. + * source_image_names is important to determine when to flush the image from a cache (dynamic media) */ - video::IImage* generateImage(const std::string &name); + video::IImage* generateImage(const std::string &name, std::set &source_image_names); // Thread-safe cache of what source images are known (true = known) MutexedMap m_source_image_existence; @@ -592,7 +611,9 @@ u32 TextureSource::generateTexture(const std::string &name) video::IVideoDriver *driver = RenderingEngine::get_video_driver(); sanity_check(driver); - video::IImage *img = generateImage(name); + // passed into texture info for dynamic media tracking + std::set source_image_names; + video::IImage *img = generateImage(name, source_image_names); video::ITexture *tex = NULL; @@ -613,7 +634,7 @@ u32 TextureSource::generateTexture(const std::string &name) MutexAutoLock lock(m_textureinfo_cache_mutex); u32 id = m_textureinfo_cache.size(); - TextureInfo ti(name, tex); + TextureInfo ti(name, tex, source_image_names); m_textureinfo_cache.push_back(ti); m_name_to_id[name] = id; @@ -677,7 +698,8 @@ Palette* TextureSource::getPalette(const std::string &name) auto it = m_palettes.find(name); if (it == m_palettes.end()) { // Create palette - video::IImage *img = generateImage(name); + std::set source_image_names; // unused, sadly. + video::IImage *img = generateImage(name, source_image_names); if (!img) { warningstream << "TextureSource::getPalette(): palette \"" << name << "\" could not be loaded." << std::endl; @@ -749,6 +771,26 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im m_sourcecache.insert(name, img, true); m_source_image_existence.set(name, true); + + // now we need to check for any textures that need updating + MutexAutoLock lock(m_textureinfo_cache_mutex); + + video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + sanity_check(driver); + + // Recreate affected textures + u32 affected = 0; + for (TextureInfo &ti : m_textureinfo_cache) { + if (ti.name.empty()) + continue; // Skip dummy entry + // If the source image was used, we need to rebuild this texture + if (ti.sourceImages.find(name) != ti.sourceImages.end()) { + rebuildTexture(driver, ti); + affected++; + } + } + if (affected > 0) + verbosestream << "TextureSource: inserting \"" << name << "\" caused rebuild of " << affected << " textures." << std::endl; } void TextureSource::rebuildImagesAndTextures() @@ -765,27 +807,38 @@ void TextureSource::rebuildImagesAndTextures() for (TextureInfo &ti : m_textureinfo_cache) { if (ti.name.empty()) continue; // Skip dummy entry - - video::IImage *img = generateImage(ti.name); -#if ENABLE_GLES - img = Align2Npot2(img, driver); -#endif - // Create texture from resulting image - video::ITexture *t = NULL; - if (img) { - t = driver->addTexture(ti.name.c_str(), img); - guiScalingCache(io::path(ti.name.c_str()), driver, img); - img->drop(); - } - video::ITexture *t_old = ti.texture; - // Replace texture - ti.texture = t; - - if (t_old) - m_texture_trash.push_back(t_old); + rebuildTexture(driver, ti); } } +void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti) +{ + if (ti.name.empty()) + return; // this shouldn't happen, just a precaution + + // replaces the previous sourceImages + // shouldn't really need to be done, but can't hurt + std::set source_image_names; + video::IImage *img = generateImage(ti.name, source_image_names); +#if ENABLE_GLES + img = Align2Npot2(img, driver); +#endif + // Create texture from resulting image + video::ITexture *t = NULL; + if (img) { + t = driver->addTexture(ti.name.c_str(), img); + guiScalingCache(io::path(ti.name.c_str()), driver, img); + img->drop(); + } + video::ITexture *t_old = ti.texture; + // Replace texture + ti.texture = t; + ti.sourceImages = source_image_names; + + if (t_old) + m_texture_trash.push_back(t_old); +} + inline static void applyShadeFactor(video::SColor &color, u32 factor) { u32 f = core::clamp(factor, 0, 256); @@ -901,7 +954,7 @@ static video::IImage *createInventoryCubeImage( return result; } -video::IImage* TextureSource::generateImage(const std::string &name) +video::IImage* TextureSource::generateImage(const std::string &name, std::set &source_image_names) { // Get the base image @@ -954,7 +1007,7 @@ video::IImage* TextureSource::generateImage(const std::string &name) using a recursive call. */ if (last_separator_pos != -1) { - baseimg = generateImage(name.substr(0, last_separator_pos)); + baseimg = generateImage(name.substr(0, last_separator_pos), source_image_names); } /* @@ -972,7 +1025,7 @@ video::IImage* TextureSource::generateImage(const std::string &name) && last_part_of_name[last_part_of_name.size() - 1] == paren_close) { std::string name2 = last_part_of_name.substr(1, last_part_of_name.size() - 2); - video::IImage *tmp = generateImage(name2); + video::IImage *tmp = generateImage(name2, source_image_names); if (!tmp) { errorstream << "generateImage(): " "Failed to generate \"" << name2 << "\"" @@ -987,7 +1040,7 @@ video::IImage* TextureSource::generateImage(const std::string &name) } else { baseimg = tmp; } - } else if (!generateImagePart(last_part_of_name, baseimg)) { + } else if (!generateImagePart(last_part_of_name, baseimg, source_image_names)) { // Generate image according to part of name errorstream << "generateImage(): " "Failed to generate \"" << last_part_of_name << "\"" @@ -1101,7 +1154,7 @@ void blitBaseImage(video::IImage* &src, video::IImage* &dst) } bool TextureSource::generateImagePart(std::string part_of_name, - video::IImage *& baseimg) + video::IImage *& baseimg, std::set &source_image_names) { const char escape = '\\'; // same as in generateImage() video::IVideoDriver *driver = RenderingEngine::get_video_driver(); @@ -1109,6 +1162,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, // Stuff starting with [ are special commands if (part_of_name.empty() || part_of_name[0] != '[') { + source_image_names.insert(part_of_name); video::IImage *image = m_sourcecache.getOrLoad(part_of_name); if (image == NULL) { if (!part_of_name.empty()) { @@ -1246,7 +1300,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, infostream<<"Adding \""< dim = img->getDimension(); core::position2d pos_base(x, y); @@ -1410,15 +1464,15 @@ bool TextureSource::generateImagePart(std::string part_of_name, std::string imagename_right = sf.next("{"); // Generate images for the faces of the cube - video::IImage *img_top = generateImage(imagename_top); - video::IImage *img_left = generateImage(imagename_left); - video::IImage *img_right = generateImage(imagename_right); + video::IImage *img_top = generateImage(imagename_top, source_image_names); + video::IImage *img_left = generateImage(imagename_left, source_image_names); + video::IImage *img_right = generateImage(imagename_right, source_image_names); if (img_top == NULL || img_left == NULL || img_right == NULL) { errorstream << "generateImagePart(): Failed to create textures" << " for inventorycube \"" << part_of_name << "\"" << std::endl; - baseimg = generateImage(imagename_top); + baseimg = generateImage(imagename_top, source_image_names); return true; } @@ -1444,7 +1498,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, if (baseimg == NULL) baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16)); - video::IImage *img = generateImage(filename); + video::IImage *img = generateImage(filename, source_image_names); if (img) { core::dimension2d dim = img->getDimension(); @@ -1526,7 +1580,7 @@ bool TextureSource::generateImagePart(std::string part_of_name, sf.next(":"); std::string filename = unescape_string(sf.next_esc(":", escape), escape); - video::IImage *img = generateImage(filename); + video::IImage *img = generateImage(filename, source_image_names); if (img) { apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0), img->getDimension()); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8a323872a..9c9d8276e 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1592,14 +1592,6 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) verbosestream << "with " << filedata.size() << " bytes "; verbosestream << "(cached=" << cached << ")" << std::endl; - if (m_media_pushed_files.count(filename) != 0) { - // Ignore (but acknowledge). Previously this was for sync purposes, - // but even in new versions media cannot be replaced at runtime. - if (m_proto_ver >= 40) - sendHaveMedia({ token }); - return; - } - if (!filedata.empty()) { // LEGACY CODEPATH // Compute and check checksum of data @@ -1618,7 +1610,6 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) // Actually load media loadMedia(filedata, filename, true); - m_media_pushed_files.insert(filename); // Cache file for the next time when this client joins the same server if (cached) @@ -1626,8 +1617,6 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) return; } - m_media_pushed_files.insert(filename); - // create a downloader for this file auto downloader(std::make_shared(cached)); m_pending_media_downloads.emplace_back(token, downloader);