diff options
author | Jonathan Neuschäfer <j.neuschaefer@gmx.net> | 2012-02-08 11:49:24 +0100 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2012-03-25 11:51:00 +0300 |
commit | 4bf5065a9cdaf55a6915e647e9b5de5281d6622f (patch) | |
tree | 227b78da9c61bb6a14857b2e6ae291ab839d092a | |
parent | 04085cad3cc154a48c72127ef816bf25ca636f74 (diff) | |
download | minetest-4bf5065a9cdaf55a6915e647e9b5de5281d6622f.tar.gz minetest-4bf5065a9cdaf55a6915e647e9b5de5281d6622f.tar.bz2 minetest-4bf5065a9cdaf55a6915e647e9b5de5281d6622f.zip |
Cache textures by checksum
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client.cpp | 118 | ||||
-rw-r--r-- | src/client.h | 5 | ||||
-rw-r--r-- | src/filecache.cpp | 117 | ||||
-rw-r--r-- | src/filecache.h | 79 | ||||
-rw-r--r-- | src/hex.h | 44 |
6 files changed, 296 insertions, 68 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd0650383..5fea0546d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -238,6 +238,7 @@ set(minetest_SRCS guiCreateWorld.cpp guiConfirmMenu.cpp client.cpp + filecache.cpp tile.cpp game.cpp main.cpp diff --git a/src/client.cpp b/src/client.cpp index 89070d66b..3a08b25c2 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "sha1.h" #include "base64.h" #include "clientmap.h" +#include "filecache.h" #include "sound.h" static std::string getTextureCacheDir() @@ -255,6 +256,7 @@ Client::Client( m_map_seed(0), m_password(password), m_access_denied(false), + m_texture_cache(getTextureCacheDir()), m_texture_receive_progress(0), m_textures_received(false), m_itemdef_received(false), @@ -1412,7 +1414,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) //read texture from cache std::string name = deSerializeString(is); std::string sha1_texture = deSerializeString(is); - + // if name contains illegal characters, ignore the texture if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){ errorstream<<"Client: ignoring illegal texture name " @@ -1420,74 +1422,50 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) continue; } - std::string tpath = getTextureCacheDir() + DIR_DELIM + name; - // Read data - std::ifstream fis(tpath.c_str(), std::ios_base::binary); - - - if(fis.good() == false){ - infostream<<"Client::Texture not found in cache: " - <<name << " expected it at: "<<tpath<<std::endl; - } - else - { - 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); - if(fis.eof()) - break; - if(!fis.good()){ - bad = true; - break; - } - } - if(bad){ - infostream<<"Client: Failed to read texture from cache\"" - <<name<<"\""<<std::endl; - } - else { + std::string sha1_decoded = base64_decode(sha1_texture); + std::ostringstream tmp_os(std::ios_base::binary); + bool tex_in_cache = m_texture_cache.loadByChecksum(name, + tmp_os, sha1_decoded); + m_texture_name_sha1_map.set(name, sha1_decoded); - SHA1 sha1; - sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length()); + if(tex_in_cache) { - unsigned char *digest = sha1.getDigest(); + SHA1 sha1; + sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length()); - std::string digest_string = base64_encode(digest, 20); + unsigned char *digest = sha1.getDigest(); - if (digest_string == sha1_texture) { - // Silly irrlicht's const-incorrectness - Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size()); + std::string digest_string = base64_encode(digest, 20); - // Create an irrlicht memory file - io::IReadFile *rfile = irrfs->createMemoryReadFile( - *data_rw, tmp_os.str().size(), "_tempreadfile"); - assert(rfile); - // Read image - video::IImage *img = vdrv->createImageFromFile(rfile); - if(!img){ - infostream<<"Client: Cannot create image from data of " - <<"received texture \""<<name<<"\""<<std::endl; - rfile->drop(); - } - else { - m_tsrc->insertSourceImage(name, img); - img->drop(); - rfile->drop(); + if (digest_string == sha1_texture) { + // Silly irrlicht's const-incorrectness + Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size()); - texture_found = true; - } + // Create an irrlicht memory file + io::IReadFile *rfile = irrfs->createMemoryReadFile( + *data_rw, tmp_os.str().size(), "_tempreadfile"); + assert(rfile); + // Read image + video::IImage *img = vdrv->createImageFromFile(rfile); + if(!img){ + infostream<<"Client: Cannot create image from data of " + <<"received texture \""<<name<<"\""<<std::endl; + rfile->drop(); } else { - infostream<<"Client::Texture cached sha1 hash not matching server hash: " - <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl; - } + m_tsrc->insertSourceImage(name, img); + img->drop(); + rfile->drop(); - free(digest); + texture_found = true; + } } + else { + infostream<<"Client::Texture cached sha1 hash not matching server hash: " + <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl; + } + + free(digest); } //add texture request @@ -1598,15 +1576,15 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) fs::CreateAllDirs(getTextureCacheDir()); - std::string filename = getTextureCacheDir() + DIR_DELIM + name; - std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc); - - if (outfile.good()) { - outfile.write(data.c_str(),data.length()); - outfile.close(); - } - else { - errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl; + { + core::map<std::string, std::string>::Node *n; + n = m_texture_name_sha1_map.find(name); + if(n == NULL) + errorstream<<"The server sent a texture that has not been announced." + <<std::endl; + else + m_texture_cache.updateByChecksum(name, + data, n->getValue()); } m_tsrc->insertSourceImage(name, img); @@ -2382,6 +2360,10 @@ void Client::afterContentReceived() assert(m_nodedef_received); assert(m_textures_received); + // remove the information about which checksum each texture + // ought to have + m_texture_name_sha1_map.clear(); + // Rebuild inherited images and recreate textures m_tsrc->rebuildImagesAndTextures(); diff --git a/src/client.h b/src/client.h index 3a47a08f6..8d7597e80 100644 --- a/src/client.h +++ b/src/client.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "inventorymanager.h" #include "filesys.h" +#include "filecache.h" struct MeshMakeData; class MapBlockMesh; @@ -366,6 +367,10 @@ private: bool m_access_denied; std::wstring m_access_denied_reason; Queue<ClientEvent> m_client_event_queue; + FileCache m_texture_cache; + // a map of the name and SHA1 checksum of each texture; + // cleared after content has been recieved + core::map<std::string, std::string> m_texture_name_sha1_map; float m_texture_receive_progress; bool m_textures_received; bool m_itemdef_received; diff --git a/src/filecache.cpp b/src/filecache.cpp new file mode 100644 index 000000000..28d6bbc80 --- /dev/null +++ b/src/filecache.cpp @@ -0,0 +1,117 @@ +/* +Minetest-c55 +Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2012 Jonathan Neuschäfer <j.neuschaefer@gmx.net> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "filecache.h" +#include "clientserver.h" +#include "log.h" +#include "filesys.h" +#include "utility.h" +#include "hex.h" + +#include <string> +#include <iostream> + +bool FileCache::loadByPath(const std::string &name, std::ostream &os, + const std::string &path) +{ + std::ifstream fis(path.c_str(), std::ios_base::binary); + + if(!fis.good()){ + infostream<<"FileCache: File not found in cache: " + <<name << " expected it at: "<<path<<std::endl; + return false; + } + + bool bad = false; + for(;;){ + char buf[1024]; + fis.read(buf, 1024); + std::streamsize len = fis.gcount(); + os.write(buf, len); + if(fis.eof()) + break; + if(!fis.good()){ + bad = true; + break; + } + } + if(bad){ + infostream<<"FileCache: Failed to read file from cache: \"" + <<path<<"\""<<std::endl; + } + + return !bad; +} + +bool FileCache::updateByPath(const std::string &name, const std::string &data, + const std::string &path) +{ + std::ofstream file(path.c_str(), std::ios_base::binary | + std::ios_base::trunc); + + if(!file.good()) + { + errorstream<<"FileCache: Can't write to file at " + <<path<<std::endl; + return false; + } + + file.write(data.c_str(), data.length()); + file.close(); + + return !file.fail(); +} + +bool FileCache::loadByName(const std::string &name, std::ostream &os) +{ + std::string path = m_dir + DIR_DELIM + name; + return loadByPath(name, os, path); +} + + +bool FileCache::updateByName(const std::string &name, const std::string &data) +{ + std::string path = m_dir + DIR_DELIM + name; + return updateByPath(name, data, path); +} + +std::string FileCache::getPathFromChecksum(const std::string &name, + const std::string &checksum) +{ + std::string checksum_hex = hex_encode(checksum.c_str(), checksum.length()); + size_t dot = name.find_last_of('.');; + std::string ext = (dot == std::string::npos)? "" : + name.substr(dot, std::string::npos); + return m_dir + DIR_DELIM + checksum_hex + ext; +} + +bool FileCache::loadByChecksum(const std::string &name, std::ostream &os, + const std::string &checksum) +{ + std::string path = getPathFromChecksum(name, checksum); + return loadByPath(name, os, path); +} + +bool FileCache::updateByChecksum(const std::string &name, + const std::string &data, const std::string &checksum) +{ + std::string path = getPathFromChecksum(name, checksum); + return updateByPath(name, data, path); +} diff --git a/src/filecache.h b/src/filecache.h new file mode 100644 index 000000000..35ae9aa7d --- /dev/null +++ b/src/filecache.h @@ -0,0 +1,79 @@ +/* +Minetest-c55 +Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2012 Jonathan Neuschäfer <j.neuschaefer@gmx.net> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef FILECACHE_HEADER +#define FILECACHE_HEADER + +#include <string> +#include <iostream> + +class FileCache +{ +public: + /* + 'dir' is the file cache directory to use. + */ + FileCache(std::string dir): + m_dir(dir) + { + } + + /* + Searches the cache for a file with a given name. + If the file is found, lookup copies it into 'os' and + returns true. Otherwise false is returned. + */ + bool loadByName(const std::string &name, std::ostream &os); + + /* + Stores a file in the cache based on its name. + Returns true on success, false otherwise. + */ + bool updateByName(const std::string &name, const std::string &data); + + /* + Loads a file based on a check sum, which may be any kind of + rather unique byte sequence. Returns true, if the file could + be written into os, false otherwise. + A file name is required to give the disk file a name that + has the right file name extension (e.g. ".png"). + */ + bool loadByChecksum(const std::string &name, std::ostream &os, + const std::string &checksum); + + /* + Stores a file in the cache based on its checksum. + Returns true on success, false otherwise. + */ + bool updateByChecksum(const std::string &name, const std::string &data, + const std::string &checksum); + +private: + std::string m_dir; + + bool loadByPath(const std::string &name, std::ostream &os, + const std::string &path); + bool updateByPath(const std::string &name, const std::string &data, + const std::string &path); + std::string getPathFromChecksum(const std::string &name, + const std::string &checksum); +}; + +#endif diff --git a/src/hex.h b/src/hex.h new file mode 100644 index 000000000..1afb87597 --- /dev/null +++ b/src/hex.h @@ -0,0 +1,44 @@ +/* +Minetest-c55 +Copyright (C) 2012 Jonathan Neuschäfer <j.neuschaefer@gmx.net> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef HEX_HEADER +#define HEX_HEADER + +#include <string> + +static const char hex_chars[] = "0123456789abcdef"; + +static std::string hex_encode(const char *data, unsigned int data_size) +{ + std::string ret; + char buf2[3]; + buf2[2] = '\0'; + + for(unsigned int i = 0; i < data_size; i++) + { + unsigned char c = (unsigned char) data[i]; + buf2[0] = hex_chars[(c & 0xf0) >> 4]; + buf2[1] = hex_chars[c & 0x0f]; + ret.append(buf2); + } + + return ret; +} + +#endif |