]> git.lizzy.rs Git - minetest.git/blobdiff - src/client.cpp
Add a callback: minetest.register_on_craft(itemstack, player,
[minetest.git] / src / client.cpp
index f27f95d98c2d939d7c8e010e28344f78ec67e90f..98c9af972dadb428f74b65e27a00ac715f2c4fdc 100644 (file)
@@ -20,15 +20,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "client.h"
 #include <iostream>
 #include "clientserver.h"
-#include "jmutexautolock.h"
+#include "jthread/jmutexautolock.h"
 #include "main.h"
 #include <sstream>
+#include "filesys.h"
 #include "porting.h"
 #include "mapsector.h"
 #include "mapblock_mesh.h"
 #include "mapblock.h"
 #include "settings.h"
 #include "profiler.h"
+#include "gettext.h"
 #include "log.h"
 #include "nodemetadata.h"
 #include "nodedef.h"
@@ -45,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "IMeshCache.h"
 #include "util/serialize.h"
 #include "config.h"
+#include "util/directiontables.h"
 
 #if USE_CURL
 #include <curl/curl.h>
@@ -248,7 +251,7 @@ void * MediaFetchThread::Thread()
                        m_file_data.push_back(make_pair(i->name, data));
                } else {
                        m_failed.push_back(*i);
-                       infostream << "cURL request failed for " << i->name << std::endl;
+                       infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
                }
                curl_easy_cleanup(curl);
        }
@@ -269,7 +272,8 @@ Client::Client(
                IWritableItemDefManager *itemdef,
                IWritableNodeDefManager *nodedef,
                ISoundManager *sound,
-               MtEventManager *event
+               MtEventManager *event,
+               bool ipv6
 ):
        m_tsrc(tsrc),
        m_shsrc(shsrc),
@@ -285,7 +289,7 @@ Client::Client(
                device->getSceneManager(),
                tsrc, this, device
        ),
-       m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
+       m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
        m_device(device),
        m_server_ser_ver(SER_FMT_VER_INVALID),
        m_playeritem(0),
@@ -317,12 +321,6 @@ Client::Client(
        m_playerpos_send_timer = 0.0;
        m_ignore_damage_timer = 0.0;
 
-       // 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;
-       
        /*
                Add local player
        */
@@ -348,6 +346,11 @@ Client::~Client()
        m_mesh_update_thread.setRun(false);
        while(m_mesh_update_thread.IsRunning())
                sleep_ms(100);
+       while(!m_mesh_update_thread.m_queue_out.empty()) {
+               MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
+               delete r.mesh;
+       }
+
 
        delete m_inventory_from_server;
 
@@ -363,6 +366,15 @@ Client::~Client()
        for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
                        i != m_media_fetch_threads.end(); ++i)
                delete *i;
+
+       // cleanup 3d model meshes on client shutdown
+       while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
+               scene::IAnimatedMesh * mesh =
+                       m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
+
+               if (mesh != NULL)
+                       m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
+       }
 }
 
 void Client::connect(Address address)
@@ -545,14 +557,14 @@ void Client::step(float dtime)
        
                        // Send TOSERVER_INIT
                        // [0] u16 TOSERVER_INIT
-                       // [2] u8 SER_FMT_VER_HIGHEST
+                       // [2] u8 SER_FMT_VER_HIGHEST_READ
                        // [3] u8[20] player_name
                        // [23] u8[28] password (new in some version)
                        // [51] u16 minimum supported network protocol version (added sometime)
                        // [53] u16 maximum supported network protocol version (added later than the previous one)
                        SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
                        writeU16(&data[0], TOSERVER_INIT);
-                       writeU8(&data[2], SER_FMT_VER_HIGHEST);
+                       writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
 
                        memset((char*)&data[3], 0, PLAYERNAME_SIZE);
                        snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
@@ -681,9 +693,14 @@ void Client::step(float dtime)
                                        m_client_event_queue.push_back(event);
                                }
                        }
+                       else if(event.type == CEE_PLAYER_BREATH)
+                       {
+                                       u16 breath = event.player_breath.amount;
+                                       sendBreath(breath);
+                       }
                }
        }
-       
+
        /*
                Print some info
        */
@@ -746,6 +763,8 @@ void Client::step(float dtime)
 
                                // Replace with the new mesh
                                block->mesh = r.mesh;
+                       } else {
+                               delete r.mesh;
                        }
                        if(r.ack_block_to_server)
                        {
@@ -784,7 +803,8 @@ void Client::step(float dtime)
                        all_stopped &= !(*thread)->IsRunning();
                        while (!(*thread)->m_file_data.empty()) {
                                std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
-                               ++m_media_received_count;
+                               if(m_media_received_count < m_media_count)
+                                       m_media_received_count++;
 
                                bool success = loadMedia(out.second, out.first);
                                if(success){
@@ -976,16 +996,28 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
        {
                verbosestream<<"Client: Storing model into Irrlicht: "
                                <<"\""<<filename<<"\""<<std::endl;
+               scene::ISceneManager *smgr = m_device->getSceneManager();
+
+               //check if mesh was already cached
+               scene::IAnimatedMesh *mesh =
+                       smgr->getMeshCache()->getMeshByName(filename.c_str());
+
+               if (mesh != NULL) {
+                       errorstream << "Multiple models with name: " << filename.c_str() <<
+                                       " found replacing previous model!" << std::endl;
+
+                       smgr->getMeshCache()->removeMesh(mesh);
+                       mesh = 0;
+               }
 
                io::IFileSystem *irrfs = m_device->getFileSystem();
                io::IReadFile *rfile = irrfs->createMemoryReadFile(
                                *data_rw, data_rw.getSize(), filename.c_str());
                assert(rfile);
                
-               scene::ISceneManager *smgr = m_device->getSceneManager();
-               scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
+               mesh = smgr->getMesh(rfile);
                smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
-               
+               rfile->drop();
                return true;
        }
 
@@ -1123,8 +1155,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                infostream<<"Client: TOCLIENT_INIT received with "
                                "deployed="<<((int)deployed&0xff)<<std::endl;
 
-               if(deployed < SER_FMT_VER_LOWEST
-                               || deployed > SER_FMT_VER_HIGHEST)
+               if(!ser_ver_supported(deployed))
                {
                        infostream<<"Client: TOCLIENT_INIT: Server sent "
                                        <<"unsupported ser_fmt_ver"<<std::endl;
@@ -1269,6 +1300,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        */
                        //infostream<<"Updating"<<std::endl;
                        block->deSerialize(istr, ser_version, false);
+                       block->deSerializeNetworkSpecific(istr);
                }
                else
                {
@@ -1278,6 +1310,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        //infostream<<"Creating new"<<std::endl;
                        block = new MapBlock(&m_env.getMap(), p, this);
                        block->deSerialize(istr, ser_version, false);
+                       block->deSerializeNetworkSpecific(istr);
                        sector->insertBlock(block);
                }
 
@@ -1324,8 +1357,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        std::istringstream is(datastring, std::ios_base::binary);
                        //t3.stop();
                        
-                       //m_env.printPlayers(infostream);
-
                        //TimeTaker t4("player get", m_device);
                        Player *player = m_env.getLocalPlayer();
                        assert(player != NULL);
@@ -1553,6 +1584,15 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        m_client_event_queue.push_back(event);
                }
        }
+       else if(command == TOCLIENT_BREATH)
+       {
+               std::string datastring((char*)&data[2], datasize-2);
+               std::istringstream is(datastring, std::ios_base::binary);
+               Player *player = m_env.getLocalPlayer();
+               assert(player != NULL);
+               u16 breath = readU16(is);
+               player->setBreath(breath) ;
+       }
        else if(command == TOCLIENT_MOVE_PLAYER)
        {
                std::string datastring((char*)&data[2], datasize-2);
@@ -1706,15 +1746,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
        }
        else if(command == TOCLIENT_MEDIA)
        {
-               if (m_media_count == 0)
-                       return;
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
 
-               // Mesh update thread must be stopped while
-               // updating content definitions
-               assert(!m_mesh_update_thread.IsRunning());
-
                /*
                        u16 command
                        u16 total number of file bunches
@@ -1729,11 +1763,31 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                */
                int num_bunches = readU16(is);
                int bunch_i = readU16(is);
-               int num_files = readU32(is);
+               u32 num_files = readU32(is);
                infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
                                <<num_bunches<<" files="<<num_files
                                <<" size="<<datasize<<std::endl;
-               for(int i=0; i<num_files; i++){
+
+               // Check total and received media count
+               assert(m_media_received_count <= m_media_count);
+               if (num_files > m_media_count - m_media_received_count) {
+                       errorstream<<"Client: Received more files than requested:"
+                               <<" total count="<<m_media_count
+                               <<" total received="<<m_media_received_count
+                               <<" bunch "<<bunch_i<<"/"<<num_bunches
+                               <<" files="<<num_files
+                               <<" size="<<datasize<<std::endl;
+                       num_files = m_media_count - m_media_received_count;
+               }
+               if (num_files == 0)
+                       return;
+
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               assert(!m_mesh_update_thread.IsRunning());
+
+               for(u32 i=0; i<num_files; i++){
+                       assert(m_media_received_count < m_media_count);
                        m_media_received_count++;
                        std::string name = deSerializeString(is);
                        std::string data = deSerializeLongString(is);
@@ -1957,7 +2011,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                event.spawn_particle.expirationtime = expirationtime;
                event.spawn_particle.size = size;
-               event.add_particlespawner.collisiondetection =
+               event.spawn_particle.collisiondetection =
                                collisiondetection;
                event.spawn_particle.texture = new std::string(texture);
 
@@ -2019,6 +2073,114 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                m_client_event_queue.push_back(event);
        }
+       else if(command == TOCLIENT_HUDADD)
+       {
+               std::string datastring((char *)&data[2], datasize - 2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               u32 id           = readU32(is);
+               u8 type          = readU8(is);
+               v2f pos          = readV2F1000(is);
+               std::string name = deSerializeString(is);
+               v2f scale        = readV2F1000(is);
+               std::string text = deSerializeString(is);
+               u32 number       = readU32(is);
+               u32 item         = readU32(is);
+               u32 dir          = readU32(is);
+               v2f align        = readV2F1000(is);
+               v2f offset       = readV2F1000(is);
+
+               ClientEvent event;
+               event.type = CE_HUDADD;
+               event.hudadd.id     = id;
+               event.hudadd.type   = type;
+               event.hudadd.pos    = new v2f(pos);
+               event.hudadd.name   = new std::string(name);
+               event.hudadd.scale  = new v2f(scale);
+               event.hudadd.text   = new std::string(text);
+               event.hudadd.number = number;
+               event.hudadd.item   = item;
+               event.hudadd.dir    = dir;
+               event.hudadd.align  = new v2f(align);
+               event.hudadd.offset = new v2f(offset);
+               m_client_event_queue.push_back(event);
+       }
+       else if(command == TOCLIENT_HUDRM)
+       {
+               std::string datastring((char *)&data[2], datasize - 2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               u32 id = readU32(is);
+
+               ClientEvent event;
+               event.type = CE_HUDRM;
+               event.hudrm.id = id;
+               m_client_event_queue.push_back(event);
+       }
+       else if(command == TOCLIENT_HUDCHANGE)
+       {       
+               std::string sdata;
+               v2f v2fdata;
+               u32 intdata = 0;
+               
+               std::string datastring((char *)&data[2], datasize - 2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               u32 id  = readU32(is);
+               u8 stat = (HudElementStat)readU8(is);
+               
+               if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
+                       stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
+                       v2fdata = readV2F1000(is);
+               else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
+                       sdata = deSerializeString(is);
+               else
+                       intdata = readU32(is);
+               
+               ClientEvent event;
+               event.type = CE_HUDCHANGE;
+               event.hudchange.id      = id;
+               event.hudchange.stat    = (HudElementStat)stat;
+               event.hudchange.v2fdata = new v2f(v2fdata);
+               event.hudchange.sdata   = new std::string(sdata);
+               event.hudchange.data    = intdata;
+               m_client_event_queue.push_back(event);
+       }
+       else if(command == TOCLIENT_HUD_SET_FLAGS)
+       {       
+               std::string datastring((char *)&data[2], datasize - 2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               Player *player = m_env.getLocalPlayer();
+               assert(player != NULL);
+
+               u32 flags = readU32(is);
+               u32 mask  = readU32(is);
+               
+               player->hud_flags &= ~mask;
+               player->hud_flags |= flags;
+       }
+       else if(command == TOCLIENT_HUD_SET_PARAM)
+       {
+               std::string datastring((char *)&data[2], datasize - 2);
+               std::istringstream is(datastring, std::ios_base::binary);
+
+               Player *player = m_env.getLocalPlayer();
+               assert(player != NULL);
+
+               u16 param         = readU16(is);
+               std::string value = deSerializeString(is);
+
+               if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
+                       s32 hotbar_itemcount = readS32((u8*) value.c_str());
+                       if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
+                               player->hud_hotbar_itemcount = hotbar_itemcount;
+               } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
+                       ((LocalPlayer *) player)->hotbar_image = value;
+               } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
+                       ((LocalPlayer *) player)->hotbar_selected_image = value;
+               }
+       }
        else
        {
                infostream<<"Client: Ignoring unknown command "
@@ -2215,6 +2377,20 @@ void Client::sendDamage(u8 damage)
        Send(0, data, true);
 }
 
+void Client::sendBreath(u16 breath)
+{
+       DSTACK(__FUNCTION_NAME);
+       std::ostringstream os(std::ios_base::binary);
+
+       writeU16(os, TOSERVER_BREATH);
+       writeU16(os, breath);
+       // Make data buffer
+       std::string s = os.str();
+       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+       // Send as reliable
+       Send(0, data, true);
+}
+
 void Client::sendRespawn()
 {
        DSTACK(__FUNCTION_NAME);
@@ -2446,6 +2622,9 @@ void Client::inventoryAction(InventoryAction *a)
                Predict some local inventory changes
        */
        a->clientApply(this, this);
+
+       // Remove it
+       delete a;
 }
 
 ClientActiveObject * Client::getSelectedActiveObject(
@@ -2501,18 +2680,9 @@ void Client::printDebugInfo(std::ostream &os)
                <<std::endl;*/
 }
 
-std::list<std::wstring> Client::getConnectedPlayerNames()
+std::list<std::string> Client::getConnectedPlayerNames()
 {
-       std::list<Player*> players = m_env.getPlayers(true);
-       std::list<std::wstring> playerNames;
-       for(std::list<Player*>::iterator
-                       i = players.begin();
-                       i != players.end(); ++i)
-       {
-               Player *player = *i;
-               playerNames.push_back(narrow_to_wide(player->getName()));
-       }
-       return playerNames;
+       return m_env.getPlayerNames();
 }
 
 float Client::getAnimationTime()
@@ -2552,6 +2722,13 @@ u16 Client::getHP()
        return player->hp;
 }
 
+u16 Client::getBreath()
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       return player->getBreath();
+}
+
 bool Client::getChatMessage(std::wstring &message)
 {
        if(m_chat_queue.size() == 0)
@@ -2639,21 +2816,14 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool
        }
        catch(InvalidPositionException &e){}
        // Leading edge
-       try{
-               v3s16 p = blockpos + v3s16(-1,0,0);
-               addUpdateMeshTask(p, false, urgent);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,-1,0);
-               addUpdateMeshTask(p, false, urgent);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,0,-1);
-               addUpdateMeshTask(p, false, urgent);
+       for (int i=0;i<6;i++)
+       {
+               try{
+                       v3s16 p = blockpos + g_6dirs[i];
+                       addUpdateMeshTask(p, false, urgent);
+               }
+               catch(InvalidPositionException &e){}
        }
-       catch(InvalidPositionException &e){}
 }
 
 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
@@ -2708,7 +2878,10 @@ ClientEvent Client::getClientEvent()
        return m_client_event_queue.pop_front();
 }
 
-void Client::afterContentReceived()
+void draw_load_screen(const std::wstring &text,
+               IrrlichtDevice* device, gui::IGUIFont* font,
+               float dtime=0 ,int percent=0, bool clouds=true);
+void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
 {
        infostream<<"Client::afterContentReceived() started"<<std::endl;
        assert(m_itemdef_received);
@@ -2723,12 +2896,8 @@ void Client::afterContentReceived()
        infostream<<"- Rebuilding images and textures"<<std::endl;
        m_tsrc->rebuildImagesAndTextures();
 
-       // Update texture atlas
-       infostream<<"- Updating texture atlas"<<std::endl;
-       if(g_settings->getBool("enable_texture_atlas"))
-               m_tsrc->buildMainAtlas(this);
-
        // Rebuild shaders
+       infostream<<"- Rebuilding shaders"<<std::endl;
        m_shsrc->rebuildShaders();
 
        // Update node aliases
@@ -2743,13 +2912,23 @@ void Client::afterContentReceived()
        if(g_settings->getBool("preload_item_visuals"))
        {
                verbosestream<<"Updating item textures and meshes"<<std::endl;
+               wchar_t* text = wgettext("Item textures...");
+               draw_load_screen(text,device,font,0,0);
                std::set<std::string> names = m_itemdef->getAll();
+               size_t size = names.size();
+               size_t count = 0;
+               int percent = 0;
                for(std::set<std::string>::const_iterator
                                i = names.begin(); i != names.end(); ++i){
                        // Asking for these caches the result
                        m_itemdef->getInventoryTexture(*i, this);
                        m_itemdef->getWieldMesh(*i, this);
+                       count++;
+                       percent = count*100/size;
+                       if (count%50 == 0) // only update every 50 item
+                               draw_load_screen(text,device,font,0,percent);
                }
+               delete[] text;
        }
 
        // Start mesh update thread after setting up content definitions