From 0e1f448b619de6651205692806c6f137f481dc43 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 2 Jan 2012 13:31:50 +0200 Subject: [PATCH] Texture cache on client (mostly made by sapier) (breaks network compatibility) --- src/client.cpp | 181 +++++++++++++++++++++++++ src/client.h | 1 + src/clientserver.h | 28 +++- src/server.cpp | 326 ++++++++++++++++++++++++++++++++------------- src/server.h | 30 ++++- 5 files changed, 471 insertions(+), 95 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index cfdc713db..e3b250b32 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -36,6 +36,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tooldef.h" #include "craftitemdef.h" #include +#include "sha1.h" +#include "base64.h" + +static std::string getTextureCacheDir() +{ + return porting::path_userdata + DIR_DELIM + "cache" + DIR_DELIM + "texture"; +} + +struct TextureRequest +{ + std::string name; + + TextureRequest(const std::string &name_=""): + name(name_) + {} +}; /* QueuedMeshUpdate @@ -1232,6 +1248,157 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.deathscreen.camera_point_target_z = camera_point_target.Z; m_client_event_queue.push_back(event); } + else if(command == TOCLIENT_ANNOUNCE_TEXTURES) + { + io::IFileSystem *irrfs = m_device->getFileSystem(); + video::IVideoDriver *vdrv = m_device->getVideoDriver(); + + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + + // Stop threads while updating content definitions + m_mesh_update_thread.setRun(false); + // Process the remaining TextureSource queue to let MeshUpdateThread + // get it's remaining textures and thus let it stop + while(m_mesh_update_thread.IsRunning()){ + m_tsrc->processQueue(); + } + + int num_textures = readU16(is); + + core::list texture_requests; + + for(int i=0; i data_rw(tmp_os.str().c_str(), tmp_os.str().size()); + + // 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 \""<drop(); + } + else { + m_tsrc->insertSourceImage(name, img); + img->drop(); + rfile->drop(); + + texture_found = true; + } + } + else { + infostream<<"Client::Texture cached sha1 hash not matching server hash: " + <"< "<::Iterator i = texture_requests.begin(); + i != texture_requests.end(); i++) { + os<name); + } + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); + infostream<<"Client: Sending request list to server " <getFileSystem(); @@ -1286,6 +1453,20 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) rfile->drop(); continue; } + + 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 <insertSourceImage(name, img); img->drop(); rfile->drop(); diff --git a/src/client.h b/src/client.h index e74fec5a7..49794acf5 100644 --- a/src/client.h +++ b/src/client.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" // For IntervalLimiter #include "gamedef.h" #include "inventorymanager.h" +#include "filesys.h" struct MeshMakeData; class IGameDef; diff --git a/src/clientserver.h b/src/clientserver.h index 4d6cd716d..3f97d3732 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -37,9 +37,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Obsolete TOSERVER_GROUND_ACTION PROTOCOL_VERSION 5: Make players to be handled mostly as ActiveObjects + PROTOCOL_VERSION 6: + Only non-cached textures are sent */ -#define PROTOCOL_VERSION 5 +#define PROTOCOL_VERSION 6 #define PROTOCOL_ID 0x4f457403 @@ -235,6 +237,19 @@ enum ToClientCommand u32 length of the next item serialized CraftiItemDefManager */ + + TOCLIENT_ANNOUNCE_TEXTURES = 0x3c, + + /* + u16 command + u32 number of textures + for each texture { + u16 length of name + string name + u16 length of sha1_digest + string sha1_digest + } + */ }; enum ToServerCommand @@ -408,6 +423,17 @@ enum ToServerCommand (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.) */ + TOSERVER_REQUEST_TEXTURES = 0x40, + + /* + u16 command + u16 number of textures requested + for each texture { + u16 length of name + string name + } + */ + }; inline SharedBuffer makePacket_TOCLIENT_TIME_OF_DAY(u16 time) diff --git a/src/server.cpp b/src/server.cpp index 3679195f3..5bd072d02 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -48,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" #include "content_abm.h" #include "mods.h" +#include "sha1.h" +#include "base64.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -929,6 +931,9 @@ Server::Server( } } + // Read Textures and calculate sha1 sums + PrepareTextures(); + // Initialize Environment m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, @@ -2110,8 +2115,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send CraftItem definitions SendCraftItemDef(m_con, peer_id, m_craftitemdef); - // Send textures - SendTextures(peer_id); + // Send texture announcement + SendTextureAnnouncement(peer_id); // Send player info to all players //SendPlayerInfos(); @@ -2825,6 +2830,26 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // ActiveObject is added to environment in AsyncRunStep after // the previous addition has been succesfully removed } + else if(command == TOSERVER_REQUEST_TEXTURES) { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + infostream<<"TOSERVER_REQUEST_TEXTURES: "< tosend; + + u16 numtextures = readU16(is); + + for(int i = 0; i < numtextures; i++) { + + std::string name = deSerializeString(is); + + tosend.push_back(TextureRequest(name)); + infostream<<"TOSERVER_REQUEST_TEXTURES: requested texture " << name <::Iterator i = m_mods.begin(); + i != m_mods.end(); i++){ + const ModSpec &mod = *i; + std::string texturepath = mod.path + DIR_DELIM + "textures"; + std::vector dirlist = fs::GetDirListing(texturepath); + for(u32 j=0; jm_Textures[tname] = TextureInformation(tpath,digest_string); + infostream<<"Server::PrepareTextures(): added sha1 for "<< tname < texture_announcements; + + for (std::map::iterator i = m_Textures.begin();i != m_Textures.end(); i++ ) { + + // Put in list + texture_announcements.push_back( + SendableTextureAnnouncement(i->first, i->second.sha1_digest)); + } + + //send announcements + + /* + u16 command + u32 number of textures + for each texture { + u16 length of name + string name + u16 length of digest string + string sha1_digest + } + */ + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_ANNOUNCE_TEXTURES); + writeU16(os, texture_announcements.size()); + + for(core::list::Iterator + j = texture_announcements.begin(); + j != texture_announcements.end(); j++){ + os<name); + os<sha1_digest); + } + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTextureAnnouncement(): Send to client"< data((u8*)s.c_str(), s.size()); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); + +} + struct SendableTexture { std::string name; @@ -4247,112 +4390,109 @@ struct SendableTexture {} }; -void Server::SendTextures(u16 peer_id) -{ +void Server::SendTexturesRequested(u16 peer_id,core::list tosend) { DSTACK(__FUNCTION_NAME); - infostream<<"Server::SendTextures(): Sending textures to client"< > texture_bunches; texture_bunches.push_back(core::list()); - + u32 texture_size_bunch_total = 0; - for(core::list::Iterator i = m_mods.begin(); - i != m_mods.end(); i++){ - const ModSpec &mod = *i; - std::string texturepath = mod.path + DIR_DELIM + "textures"; - std::vector dirlist = fs::GetDirListing(texturepath); - for(u32 j=0; j= bytes_per_bunch){ - texture_bunches.push_back(core::list()); - texture_size_bunch_total = 0; + + for(core::list::Iterator i = tosend.begin(); i != tosend.end(); i++) { + + //TODO get path + name + std::string tpath = m_Textures[(*i).name].path; + + // Read data + std::ifstream fis(tpath.c_str(), std::ios_base::binary); + if(fis.good() == false){ + errorstream<<"Server::SendTexturesRequested(): Could not open \"" + <= bytes_per_bunch){ + texture_bunches.push_back(core::list()); + texture_size_bunch_total = 0; + } + } /* Create and send packets */ - - u32 num_bunches = texture_bunches.size(); - for(u32 i=0; i::Iterator - j = texture_bunches[i].begin(); - j != texture_bunches[i].end(); j++){ - os<name); - os<data); + u32 num_bunches = texture_bunches.size(); + for(u32 i=0; i::Iterator + j = texture_bunches[i].begin(); + j != texture_bunches[i].end(); j++){ + os<name); + os<data); + } + + // Make data buffer + std::string s = os.str(); + infostream<<"Server::SendTexturesRequested(): bunch "< tosend); /* Something random @@ -744,6 +770,8 @@ private: friend class EmergeThread; friend class RemoteClient; + + std::map m_Textures; }; /*