#include "nodemetadata.h"
#include "nodedef.h"
#include "tooldef.h"
+#include <IFileSystem.h>
/*
QueuedMeshUpdate
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 "
Base for writing changes here
PROTOCOL_VERSION 4:
Add TOCLIENT_TOOLDEF
+ Add TOCLIENT_TEXTURES
*/
#define PROTOCOL_VERSION 4
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
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
*/
// 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
// 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);
}
}
/*
Send some initialization data
*/
+
+ // Send textures
+ SendTextures(peer_id);
// Send tool definitions
SendToolDef(m_con, peer_id, m_toolmgr);
}
}
+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
*/
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
// Sends blocks to clients (locks env and con on its own)
void SendBlocks(float dtime);
+
+ void SendTextures(u16 peer_id);
/*
Something random
// Configuration path ("" = no configuration file)
std::string m_configpath;
+
+ // Mod parent directory paths
+ core::list<std::string> m_modspaths;
bool m_shutdown_requested;
*/
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:
{
}
-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;
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);
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)
{
{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);