Sending of textures WIP

master
Perttu Ahola 2011-11-15 11:02:47 +02:00
parent 7bdc328a71
commit 45fc45a49e
6 changed files with 303 additions and 55 deletions

View File

@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodemetadata.h" #include "nodemetadata.h"
#include "nodedef.h" #include "nodedef.h"
#include "tooldef.h" #include "tooldef.h"
#include <IFileSystem.h>
/* /*
QueuedMeshUpdate 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.setRun(true);
m_mesh_update_thread.Start(); 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 else
{ {
infostream<<"Client: Ignoring unknown command " infostream<<"Client: Ignoring unknown command "

View File

@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Base for writing changes here Base for writing changes here
PROTOCOL_VERSION 4: PROTOCOL_VERSION 4:
Add TOCLIENT_TOOLDEF Add TOCLIENT_TOOLDEF
Add TOCLIENT_TEXTURES
*/ */
#define PROTOCOL_VERSION 4 #define PROTOCOL_VERSION 4
@ -198,6 +199,18 @@ enum ToClientCommand
serialized ToolDefManager 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, //TOCLIENT_CONTENT_SENDING_MODE = 0x38,
/* /*
u16 command u16 command

View File

@ -944,6 +944,35 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
return checksum; 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 Server
*/ */
@ -988,6 +1017,9 @@ Server::Server(
// Initialize default node definitions // Initialize default node definitions
content_mapnode_init(NULL, m_nodemgr); content_mapnode_init(NULL, m_nodemgr);
// Add default global mod path
m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
// Initialize scripting // Initialize scripting
@ -997,26 +1029,17 @@ Server::Server(
// Export API // Export API
scriptapi_export(m_lua, this); scriptapi_export(m_lua, this);
// Load and run scripts // Load and run scripts
core::list<std::string> modspaths; core::list<ModSpec> mods = getMods(m_modspaths);
modspaths.push_back(porting::path_data + DIR_DELIM + "mods"); for(core::list<ModSpec>::Iterator i = mods.begin();
for(core::list<std::string>::Iterator i = modspaths.begin(); i != mods.end(); i++){
i != modspaths.end(); i++){ ModSpec mod = *i;
std::string modspath = *i; infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath); std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
for(u32 j=0; j<dirlist.size(); j++){ bool success = script_load(m_lua, scriptpath.c_str());
if(!dirlist[j].dir) if(!success){
continue; errorstream<<"Server: Failed to load and run "
std::string modname = dirlist[j].name; <<scriptpath<<std::endl;
infostream<<"Server: Loading mod \""<<modname<<"\" script..." assert(0);
<<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);
}
} }
} }
@ -2115,6 +2138,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
/* /*
Send some initialization data Send some initialization data
*/ */
// Send textures
SendTextures(peer_id);
// Send tool definitions // Send tool definitions
SendToolDef(m_con, peer_id, m_toolmgr); 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 Something random
*/ */

View File

@ -515,7 +515,10 @@ private:
IToolDefManager *tooldef); 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 // 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) // Sends blocks to clients (locks env and con on its own)
void SendBlocks(float dtime); void SendBlocks(float dtime);
void SendTextures(u16 peer_id);
/* /*
Something random Something random
@ -682,6 +687,9 @@ private:
// Configuration path ("" = no configuration file) // Configuration path ("" = no configuration file)
std::string m_configpath; std::string m_configpath;
// Mod parent directory paths
core::list<std::string> m_modspaths;
bool m_shutdown_requested; bool m_shutdown_requested;

View File

@ -242,14 +242,6 @@ public:
*/ */
void updateAP(AtlasPointer &ap); void updateAP(AtlasPointer &ap);
/*
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. Processes queued texture requests from other threads.
@ -257,6 +249,17 @@ public:
*/ */
void processQueue(); void processQueue();
/*
Build the main texture atlas which contains most of the
textures.
*/
void buildMainAtlas(class IGameDef *gamedef);
/*
Insert an image into the cache without touching the filesystem.
*/
void insertImage(const std::string &name, video::IImage *img);
private: private:
// The id of the thread that is allowed to use irrlicht directly // The id of the thread that is allowed to use irrlicht directly
@ -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) u32 TextureSource::getTextureId(const std::string &name)
{ {
//infostream<<"getTextureId(): \""<<name<<"\""<<std::endl; //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
@ -624,6 +602,31 @@ void TextureSource::updateAP(AtlasPointer &ap)
ap = ap2; 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) void TextureSource::buildMainAtlas(class IGameDef *gamedef)
{ {
assert(gamedef->tsrc() == this); assert(gamedef->tsrc() == this);
@ -864,6 +867,46 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
driver->writeImageToFile(atlas_img, atlaspath.c_str());*/ 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, video::IImage* generate_image_from_scratch(std::string name,
IrrlichtDevice *device) IrrlichtDevice *device)
{ {

View File

@ -156,8 +156,10 @@ public:
{return NULL;} {return NULL;}
virtual void updateAP(AtlasPointer &ap){}; virtual void updateAP(AtlasPointer &ap){};
virtual void buildMainAtlas(class IGameDef *gamedef)=0;
virtual void processQueue()=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); IWritableTextureSource* createTextureSource(IrrlichtDevice *device);