]> git.lizzy.rs Git - minetest.git/blobdiff - src/client.cpp
Random Lua tweaks/fixes
[minetest.git] / src / client.cpp
index c40c9e5136db6eff9f450fca75c84cfbfca2d296..2326ff35db6735adb96e555f6bc37c18d2fa91d6 100644 (file)
@@ -30,6 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "settings.h"
 #include "profiler.h"
+#include "log.h"
+#include "nodemetadata.h"
+#include "nodedef.h"
+#include "tooldef.h"
+#include <IFileSystem.h>
 
 /*
        QueuedMeshUpdate
@@ -131,6 +136,8 @@ void * MeshUpdateThread::Thread()
 {
        ThreadStarted();
 
+       log_register_thread("MeshUpdateThread");
+
        DSTACK(__FUNCTION_NAME);
        
        BEGIN_DEBUG_EXCEPTION_HANDLER
@@ -153,17 +160,17 @@ void * MeshUpdateThread::Thread()
                        continue;
                }
 
-               ScopeProfiler sp(g_profiler, "mesh make");
+               ScopeProfiler sp(g_profiler, "Client: Mesh making");
 
                scene::SMesh *mesh_new = NULL;
-               mesh_new = makeMapBlockMesh(q->data);
+               mesh_new = makeMapBlockMesh(q->data, m_gamedef);
 
                MeshUpdateResult r;
                r.p = q->p;
                r.mesh = mesh_new;
                r.ack_block_to_server = q->ack_block_to_server;
 
-               /*dstream<<"MeshUpdateThread: Processed "
+               /*infostream<<"MeshUpdateThread: Processed "
                                <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
                                <<std::endl;*/
 
@@ -172,7 +179,7 @@ void * MeshUpdateThread::Thread()
                delete q;
        }
 
-       END_DEBUG_EXCEPTION_HANDLER
+       END_DEBUG_EXCEPTION_HANDLER(errorstream)
 
        return NULL;
 }
@@ -181,13 +188,21 @@ Client::Client(
                IrrlichtDevice *device,
                const char *playername,
                std::string password,
-               MapDrawControl &control):
-       m_mesh_update_thread(),
+               MapDrawControl &control,
+               IWritableTextureSource *tsrc,
+               IWritableToolDefManager *tooldef,
+               IWritableNodeDefManager *nodedef
+):
+       m_tsrc(tsrc),
+       m_tooldef(tooldef),
+       m_nodedef(nodedef),
+       m_mesh_update_thread(this),
        m_env(
-               new ClientMap(this, control,
+               new ClientMap(this, this, control,
                        device->getSceneManager()->getRootSceneNode(),
                        device->getSceneManager(), 666),
-               device->getSceneManager()
+               device->getSceneManager(),
+               tsrc, this
        ),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
        m_device(device),
@@ -196,7 +211,11 @@ Client::Client(
        m_time_of_day(0),
        m_map_seed(0),
        m_password(password),
-       m_access_denied(false)
+       m_access_denied(false),
+       m_texture_receive_progress(0),
+       m_textures_received(false),
+       m_tooldef_received(false),
+       m_nodedef_received(false)
 {
        m_packetcounter_timer = 0.0;
        //m_delete_unused_sectors_timer = 0.0;
@@ -205,18 +224,23 @@ Client::Client(
        m_playerpos_send_timer = 0.0;
        m_ignore_damage_timer = 0.0;
 
-       //m_env_mutex.Init();
-       //m_con_mutex.Init();
+       // Build main texture atlas, now that the GameDef exists (that is, us)
+       if(g_settings->getBool("enable_texture_atlas"))
+               m_tsrc->buildMainAtlas(this);
+       else
+               infostream<<"Not building texture atlas."<<std::endl;
+       
+       // Update node textures
+       m_nodedef->updateTextures(m_tsrc);
 
+       // Start threads after setting up content definitions
        m_mesh_update_thread.Start();
 
        /*
                Add local player
        */
        {
-               //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-
-               Player *player = new LocalPlayer();
+               Player *player = new LocalPlayer(this);
 
                player->updateName(playername);
 
@@ -243,7 +267,7 @@ void Client::connect(Address address)
 {
        DSTACK(__FUNCTION_NAME);
        //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
-       m_con.setTimeoutMs(0);
+       m_con.SetTimeoutMs(0);
        m_con.Connect(address);
 }
 
@@ -273,7 +297,7 @@ void Client::step(float dtime)
        else
                m_ignore_damage_timer = 0.0;
        
-       //dstream<<"Client steps "<<dtime<<std::endl;
+       //infostream<<"Client steps "<<dtime<<std::endl;
 
        {
                //TimeTaker timer("ReceiveAll()", m_device);
@@ -298,8 +322,8 @@ void Client::step(float dtime)
                {
                        counter = 20.0;
                        
-                       dout_client<<"Client packetcounter (20s):"<<std::endl;
-                       m_packetcounter.print(dout_client);
+                       infostream<<"Client packetcounter (20s):"<<std::endl;
+                       m_packetcounter.print(infostream);
                        m_packetcounter.clear();
                }
        }
@@ -343,9 +367,9 @@ void Client::step(float dtime)
 
                        if(deleted_blocks.size() > 0)
                        {
-                               /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
+                               /*infostream<<"Client: Deleted blocks of "<<num
                                                <<" unused sectors"<<std::endl;*/
-                               /*dstream<<DTIME<<"Client: Deleted "<<num
+                               /*infostream<<"Client: Deleted "<<num
                                                <<" unused sectors"<<std::endl;*/
                                
                                /*
@@ -424,14 +448,14 @@ void Client::step(float dtime)
                        memset((char*)&data[3], 0, PLAYERNAME_SIZE);
                        snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
 
-                       /*dstream<<"Client: sending initial password hash: \""<<m_password<<"\""
+                       /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
                                        <<std::endl;*/
 
                        memset((char*)&data[23], 0, PASSWORD_SIZE);
                        snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
                        
                        // This should be incremented in each version
-                       writeU16(&data[51], 3);
+                       writeU16(&data[51], PROTOCOL_VERSION);
 
                        // Send as unreliable
                        Send(0, data, false);
@@ -458,7 +482,7 @@ void Client::step(float dtime)
                                &deleted_blocks);
                                
                /*if(deleted_blocks.size() > 0)
-                       dstream<<"Client: Unloaded "<<deleted_blocks.size()
+                       infostream<<"Client: Unloaded "<<deleted_blocks.size()
                                        <<" unused blocks"<<std::endl;*/
                        
                /*
@@ -560,8 +584,8 @@ void Client::step(float dtime)
                        counter = 0.0;
                        //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
                        // connectedAndInitialized() is true, peer exists.
-                       con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
-                       dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
+                       float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
+                       infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
                }
        }
 
@@ -587,7 +611,7 @@ void Client::step(float dtime)
                //TimeTaker timer("** Processing mesh update result queue");
                // 0ms
                
-               /*dstream<<"Mesh update result queue size is "
+               /*infostream<<"Mesh update result queue size is "
                                <<m_mesh_update_thread.m_queue_out.size()
                                <<std::endl;*/
 
@@ -601,7 +625,7 @@ void Client::step(float dtime)
                        }
                        if(r.ack_block_to_server)
                        {
-                               /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
+                               /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
                                                <<","<<r.p.Z<<")"<<std::endl;*/
                                /*
                                        Acknowledge block
@@ -628,12 +652,12 @@ void Client::step(float dtime)
 // Virtual methods from con::PeerHandler
 void Client::peerAdded(con::Peer *peer)
 {
-       derr_client<<"Client::peerAdded(): peer->id="
+       infostream<<"Client::peerAdded(): peer->id="
                        <<peer->id<<std::endl;
 }
 void Client::deletingPeer(con::Peer *peer, bool timeout)
 {
-       derr_client<<"Client::deletingPeer(): "
+       infostream<<"Client::deletingPeer(): "
                        "Server Peer is getting deleted "
                        <<"(timeout="<<timeout<<")"<<std::endl;
 }
@@ -641,8 +665,14 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
 void Client::ReceiveAll()
 {
        DSTACK(__FUNCTION_NAME);
+       u32 start_ms = porting::getTimeMs();
        for(;;)
        {
+               // Limit time even if there would be huge amounts of data to
+               // process
+               if(porting::getTimeMs() > start_ms + 100)
+                       break;
+               
                try{
                        Receive();
                }
@@ -652,7 +682,7 @@ void Client::ReceiveAll()
                }
                catch(con::InvalidIncomingDataException &e)
                {
-                       dout_client<<DTIME<<"Client::ReceiveAll(): "
+                       infostream<<"Client::ReceiveAll(): "
                                        "InvalidIncomingDataException: what()="
                                        <<e.what()<<std::endl;
                }
@@ -662,14 +692,13 @@ void Client::ReceiveAll()
 void Client::Receive()
 {
        DSTACK(__FUNCTION_NAME);
-       u32 data_maxsize = 200000;
-       Buffer<u8> data(data_maxsize);
+       SharedBuffer<u8> data;
        u16 sender_peer_id;
        u32 datasize;
        {
                //TimeTaker t1("con mutex and receive", m_device);
                //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
-               datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
+               datasize = m_con.Receive(sender_peer_id, data);
        }
        //TimeTaker t1("ProcessData", m_device);
        ProcessData(*data, datasize, sender_peer_id);
@@ -691,7 +720,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
        ToClientCommand command = (ToClientCommand)readU16(&data[0]);
 
-       //dstream<<"Client: received command="<<command<<std::endl;
+       //infostream<<"Client: received command="<<command<<std::endl;
        m_packetcounter.add((u16)command);
        
        /*
@@ -700,23 +729,15 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
        */
        if(sender_peer_id != PEER_ID_SERVER)
        {
-               dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
+               infostream<<"Client::ProcessData(): Discarding data not "
                                "coming from server: peer_id="<<sender_peer_id
                                <<std::endl;
                return;
        }
 
-       con::Peer *peer;
-       {
-               //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
-               // All data is coming from the server
-               // PeerNotFoundException is handled by caller.
-               peer = m_con.GetPeer(PEER_ID_SERVER);
-       }
-
        u8 ser_version = m_server_ser_ver;
 
-       //dstream<<"Client received command="<<(int)command<<std::endl;
+       //infostream<<"Client received command="<<(int)command<<std::endl;
 
        if(command == TOCLIENT_INIT)
        {
@@ -725,13 +746,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                u8 deployed = data[2];
 
-               dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
+               infostream<<"Client: TOCLIENT_INIT received with "
                                "deployed="<<((int)deployed&0xff)<<std::endl;
 
                if(deployed < SER_FMT_VER_LOWEST
                                || deployed > SER_FMT_VER_HIGHEST)
                {
-                       derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
+                       infostream<<"Client: TOCLIENT_INIT: Server sent "
                                        <<"unsupported ser_fmt_ver"<<std::endl;
                        return;
                }
@@ -757,7 +778,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                {
                        // Get map seed
                        m_map_seed = readU64(&data[2+1+6]);
-                       dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
+                       infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
                }
                
                // Reply to server
@@ -788,7 +809,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
        if(ser_version == SER_FMT_VER_INVALID)
        {
-               dout_client<<DTIME<<"WARNING: Client: Server serialization"
+               infostream<<"Client: Server serialization"
                                " format invalid or not initialized."
                                " Skipping incoming command="<<command<<std::endl;
                return;
@@ -842,9 +863,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                p.Y = readS16(&data[4]);
                p.Z = readS16(&data[6]);
                
-               /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
+               /*infostream<<"Client: Thread: BLOCKDATA for ("
                                <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-               /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
+               /*infostream<<"Client: Thread: BLOCKDATA for ("
                                <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
                
                std::string datastring((char*)&data[8], datasize-8);
@@ -867,7 +888,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        /*
                                Update an existing block
                        */
-                       //dstream<<"Updating"<<std::endl;
+                       //infostream<<"Updating"<<std::endl;
                        block->deSerialize(istr, ser_version);
                }
                else
@@ -875,20 +896,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        /*
                                Create a new block
                        */
-                       //dstream<<"Creating new"<<std::endl;
-                       block = new MapBlock(&m_env.getMap(), p);
+                       //infostream<<"Creating new"<<std::endl;
+                       block = new MapBlock(&m_env.getMap(), p, this);
                        block->deSerialize(istr, ser_version);
                        sector->insertBlock(block);
-
-                       //DEBUG
-                       /*NodeMod mod;
-                       mod.type = NODEMOD_CHANGECONTENT;
-                       mod.param = CONTENT_MESE;
-                       block->setTempMod(v3s16(8,10,8), mod);
-                       block->setTempMod(v3s16(8,9,8), mod);
-                       block->setTempMod(v3s16(8,8,8), mod);
-                       block->setTempMod(v3s16(8,7,8), mod);
-                       block->setTempMod(v3s16(8,6,8), mod);*/
                }
 
 #if 0
@@ -921,12 +932,12 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                /*
                        Add it to mesh update queue and set it to be acknowledged after update.
                */
-               //std::cerr<<"Adding mesh update task for received block"<<std::endl;
+               //infostream<<"Adding mesh update task for received block"<<std::endl;
                addUpdateMeshTaskWithEdge(p, true);
        }
        else if(command == TOCLIENT_PLAYERPOS)
        {
-               dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
+               infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
                                <<std::endl;
                /*u16 our_peer_id;
                {
@@ -935,7 +946,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                }
                // Cancel if we don't have a peer id
                if(our_peer_id == PEER_ID_INEXISTENT){
-                       dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
+                       infostream<<"TOCLIENT_PLAYERPOS cancelled: "
                                        "we have no peer id"
                                        <<std::endl;
                        return;
@@ -972,7 +983,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                v3s32 ss = readV3S32(&data[start+2+12]);
                                s32 pitch_i = readS32(&data[start+2+12+12]);
                                s32 yaw_i = readS32(&data[start+2+12+12+4]);
-                               /*dstream<<"Client: got "
+                               /*infostream<<"Client: got "
                                                <<"pitch_i="<<pitch_i
                                                <<" yaw_i="<<yaw_i<<std::endl;*/
                                f32 pitch = (f32)pitch_i / 100.0;
@@ -984,7 +995,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                player->setPitch(pitch);
                                player->setYaw(yaw);
 
-                               /*dstream<<"Client: player "<<peer_id
+                               /*infostream<<"Client: player "<<peer_id
                                                <<" pitch="<<pitch
                                                <<" yaw="<<yaw<<std::endl;*/
 
@@ -1001,13 +1012,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                }
                // Cancel if we don't have a peer id
                if(our_peer_id == PEER_ID_INEXISTENT){
-                       dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
+                       infostream<<"TOCLIENT_PLAYERINFO cancelled: "
                                        "we have no peer id"
                                        <<std::endl;
                        return;
                }
                
-               //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
+               //infostream<<"Client: Server reports players:"<<std::endl;
 
                { //envlock
                        //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
@@ -1026,7 +1037,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                                players_alive.push_back(peer_id);
                                
-                               /*dstream<<DTIME<<"peer_id="<<peer_id
+                               /*infostream<<"peer_id="<<peer_id
                                                <<" name="<<((char*)&data[start+2])<<std::endl;*/
 
                                // Don't update the info of the local player
@@ -1041,13 +1052,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                // Create a player if it doesn't exist
                                if(player == NULL)
                                {
-                                       player = new RemotePlayer(
+                                       player = new RemotePlayer(this,
                                                        m_device->getSceneManager()->getRootSceneNode(),
                                                        m_device,
                                                        -1);
                                        player->peer_id = peer_id;
                                        m_env.addPlayer(player);
-                                       dout_client<<DTIME<<"Client: Adding new player "
+                                       infostream<<"Client: Adding new player "
                                                        <<peer_id<<std::endl;
                                }
                                
@@ -1060,7 +1071,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                Remove those players from the environment that
                                weren't listed by the server.
                        */
-                       //dstream<<DTIME<<"Removing dead players"<<std::endl;
+                       //infostream<<"Removing dead players"<<std::endl;
                        core::list<Player*> players = m_env.getPlayers();
                        core::list<Player*>::Iterator ip;
                        for(ip=players.begin(); ip!=players.end(); ip++)
@@ -1072,7 +1083,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                // Warn about a special case
                                if((*ip)->peer_id == 0)
                                {
-                                       dstream<<DTIME<<"WARNING: Client: Removing "
+                                       infostream<<"Client: Removing "
                                                        "dead player with id=0"<<std::endl;
                                }
 
@@ -1086,11 +1097,11 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                                break;
                                        }
                                }
-                               /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
+                               /*infostream<<"peer_id="<<((*ip)->peer_id)
                                                <<" is_alive="<<is_alive<<std::endl;*/
                                if(is_alive)
                                        continue;
-                               dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
+                               infostream<<"Removing dead player "<<(*ip)->peer_id
                                                <<std::endl;
                                m_env.removePlayer((*ip)->peer_id);
                        }
@@ -1098,7 +1109,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
        }
        else if(command == TOCLIENT_SECTORMETA)
        {
-               dstream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
+               infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
 #if 0
                /*
                        [0] u16 command
@@ -1108,7 +1119,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                if(datasize < 3)
                        return;
 
-               //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
+               //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
 
                { //envlock
                        //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
@@ -1121,14 +1132,14 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        is.read((char*)buf, 1);
                        u16 sector_count = readU8(buf);
                        
-                       //dstream<<"sector_count="<<sector_count<<std::endl;
+                       //infostream<<"sector_count="<<sector_count<<std::endl;
 
                        for(u16 i=0; i<sector_count; i++)
                        {
                                // Read position
                                is.read((char*)buf, 4);
                                v2s16 pos = readV2S16(buf);
-                               /*dstream<<"Client: deserializing sector at "
+                               /*infostream<<"Client: deserializing sector at "
                                                <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
                                // Create sector
                                assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
@@ -1154,7 +1165,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        std::istringstream is(datastring, std::ios_base::binary);
                        //t3.stop();
                        
-                       //m_env.printPlayers(dstream);
+                       //m_env.printPlayers(infostream);
 
                        //TimeTaker t4("player get", m_device);
                        Player *player = m_env.getLocalPlayer();
@@ -1162,13 +1173,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        //t4.stop();
 
                        //TimeTaker t1("inventory.deSerialize()", m_device);
-                       player->inventory.deSerialize(is);
+                       player->inventory.deSerialize(is, this);
                        //t1.stop();
 
                        m_inventory_updated = true;
 
-                       //dstream<<"Client got player inventory:"<<std::endl;
-                       //player->inventory.print(dstream);
+                       //infostream<<"Client got player inventory:"<<std::endl;
+                       //player->inventory.print(infostream);
                }
        }
        //DEBUG
@@ -1233,7 +1244,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                // Read active block count
                u16 blockcount = readU16(is);
                if(blockcount != 0){
-                       dstream<<"WARNING: TOCLIENT_OBJECTDATA: blockcount != 0 "
+                       infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
                                        "not supported"<<std::endl;
                        return;
                }
@@ -1245,7 +1256,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                
                u16 time_of_day = readU16(&data[2]);
                time_of_day = time_of_day % 24000;
-               //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
+               //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
                
                /*
                        time_of_day:
@@ -1257,7 +1268,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                        u32 dr = m_env.getDayNightRatio();
 
-                       dstream<<"Client: time_of_day="<<time_of_day
+                       infostream<<"Client: time_of_day="<<time_of_day
                                        <<", dr="<<dr
                                        <<std::endl;
                }
@@ -1285,7 +1296,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        message += (wchar_t)readU16(buf);
                }
 
-               /*dstream<<"Client received chat message: "
+               /*infostream<<"Client received chat message: "
                                <<wide_to_narrow(message)<<std::endl;*/
                
                m_chat_queue.push_back(message);
@@ -1414,7 +1425,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                /*player->setPitch(pitch);
                player->setYaw(yaw);*/
 
-               dstream<<"Client got TOCLIENT_MOVE_PLAYER"
+               infostream<<"Client got TOCLIENT_MOVE_PLAYER"
                                <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
                                <<" pitch="<<pitch
                                <<" yaw="<<yaw
@@ -1449,13 +1460,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                        if (player == NULL)
                        {
-                               dout_client<<DTIME<<"Client: ignoring player item "
+                               infostream<<"Client: ignoring player item "
                                        << deSerializeString(is)
                                        << " for non-existing peer id " << peer_id
                                        << std::endl;
                                continue;
                        } else if (player->isLocal()) {
-                               dout_client<<DTIME<<"Client: ignoring player item "
+                               infostream<<"Client: ignoring player item "
                                        << deSerializeString(is)
                                        << " for local player" << std::endl;
                                continue;
@@ -1464,15 +1475,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                                std::string itemstring(deSerializeString(is));
                                if (itemstring.empty()) {
                                        inv->deleteItem(0);
-                                       dout_client<<DTIME
+                                       infostream
                                                <<"Client: empty player item for peer "
                                                << peer_id << std::endl;
                                } else {
                                        std::istringstream iss(itemstring);
-                                       delete inv->changeItem(0, InventoryItem::deSerialize(iss));
-                                       dout_client<<DTIME<<"Client: player item for peer " << peer_id << ": ";
-                                       player->getWieldItem()->serialize(dout_client);
-                                       dout_client<<std::endl;
+                                       delete inv->changeItem(0,
+                                                       InventoryItem::deSerialize(iss, this));
+                                       infostream<<"Client: player item for peer " << peer_id << ": ";
+                                       player->getWieldItem()->serialize(infostream);
+                                       infostream<<std::endl;
                                }
                        }
                }
@@ -1493,9 +1505,132 @@ 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_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.stop();
+               
+               /*
+                       u16 command
+                       u16 total number of texture bunches
+                       u16 index of this bunch
+                       u32 number of textures in this bunch
+                       for each texture {
+                               u16 length of name
+                               string name
+                               u32 length of data
+                               data
+                       }
+               */
+               int num_bunches = readU16(is);
+               int bunch_i = readU16(is);
+               m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
+               if(bunch_i == num_bunches - 1)
+                       m_textures_received = true;
+               int num_textures = readU32(is);
+               infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
+                               <<num_bunches<<" textures="<<num_textures
+                               <<" size="<<datasize<<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->insertSourceImage(name, img);
+                       img->drop();
+                       rfile->drop();
+               }
+               
+               if(m_nodedef_received && m_textures_received){
+                       // Rebuild inherited images and recreate textures
+                       m_tsrc->rebuildImagesAndTextures();
+
+                       // Update texture atlas
+                       if(g_settings->getBool("enable_texture_atlas"))
+                               m_tsrc->buildMainAtlas(this);
+                       
+                       // Update node textures
+                       m_nodedef->updateTextures(m_tsrc);
+               }
+
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+
+               ClientEvent event;
+               event.type = CE_TEXTURES_UPDATED;
+               m_client_event_queue.push_back(event);
+       }
+       else if(command == TOCLIENT_TOOLDEF)
+       {
+               infostream<<"Client: Received tool definitions: packet size: "
+                               <<datasize<<std::endl;
+
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               m_tooldef_received = true;
+
+               // Stop threads while updating content definitions
+               m_mesh_update_thread.stop();
+
+               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+               m_tooldef->deSerialize(tmp_is);
+               
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+       }
+       else if(command == TOCLIENT_NODEDEF)
+       {
+               infostream<<"Client: Received node definitions: packet size: "
+                               <<datasize<<std::endl;
+
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               m_nodedef_received = true;
+
+               // Stop threads while updating content definitions
+               m_mesh_update_thread.stop();
+
+               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+               m_nodedef->deSerialize(tmp_is, this);
+               
+               if(m_textures_received){
+                       // Update texture atlas
+                       if(g_settings->getBool("enable_texture_atlas"))
+                               m_tsrc->buildMainAtlas(this);
+                       
+                       // Update node textures
+                       m_nodedef->updateTextures(m_tsrc);
+               }
+
+               // Resume threads
+               m_mesh_update_thread.setRun(true);
+               m_mesh_update_thread.Start();
+       }
        else
        {
-               dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
+               infostream<<"Client: Ignoring unknown command "
                                <<command<<std::endl;
        }
 }
@@ -1510,7 +1645,7 @@ void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
                v3s16 nodepos_oversurface, u16 item)
 {
        if(connectedAndInitialized() == false){
-               dout_client<<DTIME<<"Client::groundAction() "
+               infostream<<"Client::groundAction() "
                                "cancelled (not connected)"
                                <<std::endl;
                return;
@@ -1542,7 +1677,7 @@ void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
 {
        if(connectedAndInitialized() == false){
-               dout_client<<DTIME<<"Client::clickActiveObject() "
+               infostream<<"Client::clickActiveObject() "
                                "cancelled (not connected)"
                                <<std::endl;
                return;
@@ -1954,12 +2089,12 @@ Inventory* Client::getInventory(InventoryContext *c, std::string id)
                NodeMetadata* meta = getNodeMetadata(p);
                if(meta)
                        return meta->getInventory();
-               dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
+               infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
                                <<"no metadata found"<<std::endl;
                return NULL;
        }
 
-       dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
+       infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
        return NULL;
 }
 void Client::inventoryAction(InventoryAction *a)
@@ -1977,7 +2112,7 @@ ClientActiveObject * Client::getSelectedActiveObject(
 
        m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
 
-       //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
+       //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
        
        // Sort them.
        // After this, the closest object is the first in the array.
@@ -2000,12 +2135,12 @@ ClientActiveObject * Client::getSelectedActiveObject(
 
                if(offsetted_box.intersectsWithLine(shootline_on_map))
                {
-                       //dstream<<"Returning selected object"<<std::endl;
+                       //infostream<<"Returning selected object"<<std::endl;
                        return obj;
                }
        }
 
-       //dstream<<"No object selected; returning NULL."<<std::endl;
+       //infostream<<"No object selected; returning NULL."<<std::endl;
        return NULL;
 }
 
@@ -2069,7 +2204,7 @@ void Client::clearTempMod(v3s16 p)
 
 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
 {
-       /*dstream<<"Client::addUpdateMeshTask(): "
+       /*infostream<<"Client::addUpdateMeshTask(): "
                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                        <<std::endl;*/
 
@@ -2096,7 +2231,7 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
        // Add task to queue
        m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
 
-       /*dstream<<"Mesh update input queue size is "
+       /*infostream<<"Mesh update input queue size is "
                        <<m_mesh_update_thread.m_queue_in.size()
                        <<std::endl;*/
        
@@ -2123,7 +2258,7 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
 {
        /*{
                v3s16 p = blockpos;
-               dstream<<"Client::addUpdateMeshTaskWithEdge(): "
+               infostream<<"Client::addUpdateMeshTaskWithEdge(): "
                                <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                                <<std::endl;
        }*/
@@ -2163,4 +2298,39 @@ ClientEvent Client::getClientEvent()
        return m_client_event_queue.pop_front();
 }
 
+float Client::getRTT(void)
+{
+       try{
+               return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
+       } catch(con::PeerNotFoundException &e){
+               return 1337;
+       }
+}
+
+// IGameDef interface
+// Under envlock
+IToolDefManager* Client::getToolDefManager()
+{
+       return m_tooldef;
+}
+INodeDefManager* Client::getNodeDefManager()
+{
+       return m_nodedef;
+}
+ICraftDefManager* Client::getCraftDefManager()
+{
+       return NULL;
+       //return m_craftdef;
+}
+ITextureSource* Client::getTextureSource()
+{
+       return m_tsrc;
+}
+u16 Client::allocateUnknownNodeId(const std::string &name)
+{
+       errorstream<<"Client::allocateUnknownNodeId(): "
+                       <<"Client cannot allocate node IDs"<<std::endl;
+       assert(0);
+       return CONTENT_IGNORE;
+}