diff options
-rw-r--r-- | src/client.cpp | 57 | ||||
-rw-r--r-- | src/clientserver.h | 13 | ||||
-rw-r--r-- | src/server.cpp | 165 | ||||
-rw-r--r-- | src/server.h | 10 | ||||
-rw-r--r-- | src/tile.cpp | 105 | ||||
-rw-r--r-- | src/tile.h | 4 |
6 files changed, 301 insertions, 53 deletions
diff --git a/src/client.cpp b/src/client.cpp index 1daeeba36..c89273a80 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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 " diff --git a/src/clientserver.h b/src/clientserver.h index ef8188b2a..0d553f769 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -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 diff --git a/src/server.cpp b/src/server.cpp index 9a7f1e972..44c66447c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 */ diff --git a/src/server.h b/src/server.h index 0354abbd9..e53bf9c9a 100644 --- a/src/server.h +++ b/src/server.h @@ -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; diff --git a/src/tile.cpp b/src/tile.cpp index eb3616f02..8ab92d105 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -243,19 +243,22 @@ 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) { diff --git a/src/tile.h b/src/tile.h index 105692c10..17ba74e33 100644 --- a/src/tile.h +++ b/src/tile.h @@ -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); |