Sending of textures WIP
authorPerttu Ahola <celeron55@gmail.com>
Tue, 15 Nov 2011 09:02:47 +0000 (11:02 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 29 Nov 2011 17:13:43 +0000 (19:13 +0200)
src/client.cpp
src/clientserver.h
src/server.cpp
src/server.h
src/tile.cpp
src/tile.h

index 1daeeba36fde0f73130885204d8c5465d3604958..c89273a800f90a652aa5039359ab5966d30992d1 100644 (file)
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodemetadata.h"
 #include "nodedef.h"
 #include "tooldef.h"
+#include <IFileSystem.h>
 
 /*
        QueuedMeshUpdate
@@ -1523,6 +1524,62 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                m_mesh_update_thread.setRun(true);
                m_mesh_update_thread.Start();
        }
+       else if(command == TOCLIENT_TEXTURES)
+       {
+               infostream<<"Client: Received textures: packet size: "<<datasize
+                               <<std::endl;
+
+               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.stop();
+               
+               /*
+                       u16 command
+                       u32 number of textures
+                       for each texture {
+                               u16 length of name
+                               string name
+                               u32 length of data
+                               data
+                       }
+               */
+               int num_textures = readU32(is);
+               infostream<<"Client: Received textures: count: "<<num_textures
+                               <<std::endl;
+               for(int i=0; i<num_textures; i++){
+                       std::string name = deSerializeString(is);
+                       std::string data = deSerializeLongString(is);
+                       // Silly irrlicht's const-incorrectness
+                       Buffer<char> data_rw(data.c_str(), data.size());
+                       // Create an irrlicht memory file
+                       io::IReadFile *rfile = irrfs->createMemoryReadFile(
+                                       *data_rw, data.size(), "_tempreadfile");
+                       assert(rfile);
+                       // Read image
+                       video::IImage *img = vdrv->createImageFromFile(rfile);
+                       if(!img){
+                               errorstream<<"Client: Cannot create image from data of "
+                                               <<"received texture \""<<name<<"\""<<std::endl;
+                               rfile->drop();
+                               continue;
+                       }
+                       m_tsrc->insertImage(name, img);
+                       rfile->drop();
+               }
+
+               // Update texture atlas
+               if(g_settings->getBool("enable_texture_atlas"))
+                       m_tsrc->buildMainAtlas(this);
+               
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
index ef8188b2a44476acd8787db2d784b07aa924cb52..0d553f7697b1ccd7f077b465cf57ecb39a64bc44 100644 (file)
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Base for writing changes here
        PROTOCOL_VERSION 4:
                Add TOCLIENT_TOOLDEF
+               Add TOCLIENT_TEXTURES
 */
 
 #define PROTOCOL_VERSION 4
@@ -198,6 +199,18 @@ enum ToClientCommand
                serialized ToolDefManager
        */
        
+       TOCLIENT_TEXTURES = 0x39,
+       /*
+               u16 command
+               u32 number of textures
+               for each texture {
+                       u16 length of name
+                       string name
+                       u32 length of data
+                       data
+               }
+       */
+       
        //TOCLIENT_CONTENT_SENDING_MODE = 0x38,
        /*
                u16 command
index 9a7f1e97218b7e7f152ed8f05a74390222b6095c..44c66447c3eda6a35bde5f5e283759ca59bbe10d 100644 (file)
@@ -944,6 +944,35 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
        return checksum;
 }
 
+struct ModSpec
+{
+       std::string name;
+       std::string path;
+
+       ModSpec(const std::string &name_="", const std::string path_=""):
+               name(name_),
+               path(path_)
+       {}
+};
+
+static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
+{
+       core::list<ModSpec> mods;
+       for(core::list<std::string>::Iterator i = modspaths.begin();
+                       i != modspaths.end(); i++){
+               std::string modspath = *i;
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(!dirlist[j].dir)
+                               continue;
+                       std::string modname = dirlist[j].name;
+                       std::string modpath = modspath + DIR_DELIM + modname;
+                       mods.push_back(ModSpec(modname, modpath));
+               }
+       }
+       return mods;
+}
+
 /*
        Server
 */
@@ -988,6 +1017,9 @@ Server::Server(
        
        // Initialize default node definitions
        content_mapnode_init(NULL, m_nodemgr);
+       
+       // Add default global mod path
+       m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
 
        // Initialize scripting
        
@@ -997,26 +1029,17 @@ Server::Server(
        // Export API
        scriptapi_export(m_lua, this);
        // Load and run scripts
-       core::list<std::string> modspaths;
-       modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
-       for(core::list<std::string>::Iterator i = modspaths.begin();
-                       i != modspaths.end(); i++){
-               std::string modspath = *i;
-               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
-               for(u32 j=0; j<dirlist.size(); j++){
-                       if(!dirlist[j].dir)
-                               continue;
-                       std::string modname = dirlist[j].name;
-                       infostream<<"Server: Loading mod \""<<modname<<"\" script..."
-                                       <<std::endl;
-                       std::string scriptpath = modspath + DIR_DELIM + modname
-                                       + DIR_DELIM + "init.lua";
-                       bool success = script_load(m_lua, scriptpath.c_str());
-                       if(!success){
-                               errorstream<<"Server: Failed to load and run "
-                                               <<scriptpath<<std::endl;
-                               assert(0);
-                       }
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
+               std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
+               bool success = script_load(m_lua, scriptpath.c_str());
+               if(!success){
+                       errorstream<<"Server: Failed to load and run "
+                                       <<scriptpath<<std::endl;
+                       assert(0);
                }
        }
        
@@ -2115,6 +2138,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                /*
                        Send some initialization data
                */
+
+               // Send textures
+               SendTextures(peer_id);
                
                // Send tool definitions
                SendToolDef(m_con, peer_id, m_toolmgr);
@@ -4080,6 +4106,105 @@ void Server::SendBlocks(float dtime)
        }
 }
 
+struct SendableTexture
+{
+       std::string name;
+       std::string path;
+       std::string data;
+
+       SendableTexture(const std::string &name_="", const std::string path_="",
+                       const std::string &data_=""):
+               name(name_),
+               path(path_),
+               data(data_)
+       {}
+};
+
+void Server::SendTextures(u16 peer_id)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
+       
+       /* Read textures */
+       
+       core::list<SendableTexture> textures;
+       core::list<ModSpec> mods = getMods(m_modspaths);
+       for(core::list<ModSpec>::Iterator i = mods.begin();
+                       i != mods.end(); i++){
+               ModSpec mod = *i;
+               std::string texturepath = mod.path + DIR_DELIM + "textures";
+               std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
+               for(u32 j=0; j<dirlist.size(); j++){
+                       if(dirlist[j].dir) // Ignode dirs
+                               continue;
+                       std::string tname = dirlist[j].name;
+                       std::string tpath = texturepath + DIR_DELIM + tname;
+                       // Read data
+                       std::ifstream fis(tpath.c_str(), std::ios_base::binary);
+                       if(fis.good() == false){
+                               errorstream<<"Server::SendTextures(): Could not open \""
+                                               <<tname<<"\" 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);
+                               if(fis.eof())
+                                       break;
+                               if(!fis.good()){
+                                       bad = true;
+                                       break;
+                               }
+                       }
+                       if(bad){
+                               errorstream<<"Server::SendTextures(): Failed to read \""
+                                               <<tname<<"\""<<std::endl;
+                               continue;
+                       }
+                       errorstream<<"Server::SendTextures(): Loaded \""
+                                       <<tname<<"\""<<std::endl;
+                       // Put in list
+                       textures.push_back(SendableTexture(tname, tpath, tmp_os.str()));
+               }
+       }
+
+       /* Create and send packet */
+
+       /*
+               u16 command
+               u32 number of textures
+               for each texture {
+                       u16 length of name
+                       string name
+                       u32 length of data
+                       data
+               }
+       */
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOCLIENT_TEXTURES);
+       writeU32(os, textures.size());
+       
+       for(core::list<SendableTexture>::Iterator i = textures.begin();
+                       i != textures.end(); i++){
+               os<<serializeString(i->name);
+               os<<serializeLongString(i->data);
+       }
+       
+       // Make data buffer
+       std::string s = os.str();
+       infostream<<"Server::SendTextures(): number of textures: "
+                       <<textures.size()<<", data size: "<<s.size()<<std::endl;
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       m_con.Send(peer_id, 0, data, true);
+}
+
 /*
        Something random
 */
index 0354abbd9ca842a42107b486149dd442c14abd9a..e53bf9c9aecb9d7a07976fd0920bbc89902815c3 100644 (file)
@@ -515,7 +515,10 @@ private:
                        IToolDefManager *tooldef);
        
        /*
-               Non-static send methods
+               Non-static send methods.
+               Conlock should be always used.
+               Envlock usage is documented badly but it's easy to figure out
+               which ones access the environment.
        */
 
        // Envlock and conlock should be locked when calling these
@@ -546,6 +549,8 @@ private:
        
        // Sends blocks to clients (locks env and con on its own)
        void SendBlocks(float dtime);
+       
+       void SendTextures(u16 peer_id);
 
        /*
                Something random
@@ -682,6 +687,9 @@ private:
 
        // Configuration path ("" = no configuration file)
        std::string m_configpath;
+       
+       // Mod parent directory paths
+       core::list<std::string> m_modspaths;
 
        bool m_shutdown_requested;
        
index eb3616f026531eea675ce5d5ffeb7a378777e19c..8ab92d10548e6d9e1639118fd296045f20cc7a50 100644 (file)
@@ -242,20 +242,23 @@ public:
        */
        void updateAP(AtlasPointer &ap);
 
+       /*
+               Processes queued texture requests from other threads.
+
+               Shall be called from the main thread.
+       */
+       void processQueue();
+       
        /*
                Build the main texture atlas which contains most of the
                textures.
-               
-               This is called by the constructor.
        */
        void buildMainAtlas(class IGameDef *gamedef);
        
        /*
-               Processes queued texture requests from other threads.
-
-               Shall be called from the main thread.
+               Insert an image into the cache without touching the filesystem.
        */
-       void processQueue();
+       void insertImage(const std::string &name, video::IImage *img);
        
 private:
        
@@ -305,31 +308,6 @@ TextureSource::~TextureSource()
 {
 }
 
-void TextureSource::processQueue()
-{
-       /*
-               Fetch textures
-       */
-       if(m_get_texture_queue.size() > 0)
-       {
-               GetRequest<std::string, u32, u8, u8>
-                               request = m_get_texture_queue.pop();
-
-               infostream<<"TextureSource::processQueue(): "
-                               <<"got texture request with "
-                               <<"name=\""<<request.key<<"\""
-                               <<std::endl;
-
-               GetResult<std::string, u32, u8, u8>
-                               result;
-               result.key = request.key;
-               result.callers = request.callers;
-               result.item = getTextureIdDirect(request.key);
-
-               request.dest->push_back(result);
-       }
-}
-
 u32 TextureSource::getTextureId(const std::string &name)
 {
        //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
@@ -624,6 +602,31 @@ void TextureSource::updateAP(AtlasPointer &ap)
        ap = ap2;
 }
 
+void TextureSource::processQueue()
+{
+       /*
+               Fetch textures
+       */
+       if(m_get_texture_queue.size() > 0)
+       {
+               GetRequest<std::string, u32, u8, u8>
+                               request = m_get_texture_queue.pop();
+
+               infostream<<"TextureSource::processQueue(): "
+                               <<"got texture request with "
+                               <<"name=\""<<request.key<<"\""
+                               <<std::endl;
+
+               GetResult<std::string, u32, u8, u8>
+                               result;
+               result.key = request.key;
+               result.callers = request.callers;
+               result.item = getTextureIdDirect(request.key);
+
+               request.dest->push_back(result);
+       }
+}
+
 void TextureSource::buildMainAtlas(class IGameDef *gamedef) 
 {
        assert(gamedef->tsrc() == this);
@@ -864,6 +867,46 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
        driver->writeImageToFile(atlas_img, atlaspath.c_str());*/
 }
 
+void TextureSource::insertImage(const std::string &name, video::IImage *img)
+{
+       infostream<<"TextureSource::insertImage(): name="<<name<<std::endl;
+       
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       // Create texture
+       video::ITexture *t = driver->addTexture(name.c_str(), img);
+
+       bool reuse_old_id = false;
+       u32 id = m_atlaspointer_cache.size();
+       // Check old id without fetching a texture
+       core::map<std::string, u32>::Node *n;
+       n = m_name_to_id.find(name);
+       // If it exists, we will replace the old definition
+       if(n){
+               id = n->getValue();
+               reuse_old_id = true;
+       }
+       
+       // Create AtlasPointer
+       AtlasPointer ap(id);
+       ap.atlas = t;
+       ap.pos = v2f(0,0);
+       ap.size = v2f(1,1);
+       ap.tiled = 0;
+       core::dimension2d<u32> dim = img->getDimension();
+
+       // Create SourceAtlasPointer and add to containers
+       SourceAtlasPointer nap(name, ap, img, v2s32(0,0), dim);
+       if(reuse_old_id)
+               m_atlaspointer_cache[id] = nap;
+       else
+               m_atlaspointer_cache.push_back(nap);
+       m_name_to_id[name] = id;
+}
+       
 video::IImage* generate_image_from_scratch(std::string name,
                IrrlichtDevice *device)
 {
index 105692c10bbf7f849602ce19fb3d3e4b6feb4415..17ba74e3396d6633feea8e1ed063d278cbda3e96 100644 (file)
@@ -156,8 +156,10 @@ public:
                {return NULL;}
        virtual void updateAP(AtlasPointer &ap){};
 
-       virtual void buildMainAtlas(class IGameDef *gamedef)=0;
        virtual void processQueue()=0;
+       virtual void buildMainAtlas(class IGameDef *gamedef)=0;
+       // img is eaten, do not drop it
+       virtual void insertImage(const std::string &name, video::IImage *img)=0;
 };
 
 IWritableTextureSource* createTextureSource(IrrlichtDevice *device);