LCOV - code coverage report
Current view: top level - src - client.cpp (source / functions) Hit Total Coverage
Test: report Lines: 587 939 62.5 %
Date: 2015-07-11 18:23:49 Functions: 61 88 69.3 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include <iostream>
      21             : #include <algorithm>
      22             : #include <sstream>
      23             : #include <IFileSystem.h>
      24             : #include "jthread/jmutexautolock.h"
      25             : #include "util/auth.h"
      26             : #include "util/directiontables.h"
      27             : #include "util/pointedthing.h"
      28             : #include "util/serialize.h"
      29             : #include "util/string.h"
      30             : #include "util/srp.h"
      31             : #include "client.h"
      32             : #include "network/clientopcodes.h"
      33             : #include "filesys.h"
      34             : #include "porting.h"
      35             : #include "mapblock_mesh.h"
      36             : #include "mapblock.h"
      37             : #include "minimap.h"
      38             : #include "settings.h"
      39             : #include "profiler.h"
      40             : #include "gettext.h"
      41             : #include "log.h"
      42             : #include "nodemetadata.h"
      43             : #include "itemdef.h"
      44             : #include "shader.h"
      45             : #include "clientmap.h"
      46             : #include "clientmedia.h"
      47             : #include "sound.h"
      48             : #include "IMeshCache.h"
      49             : #include "config.h"
      50             : #include "version.h"
      51             : #include "drawscene.h"
      52             : #include "database-sqlite3.h"
      53             : #include "serialization.h"
      54             : #include "guiscalingfilter.h"
      55             : 
      56             : extern gui::IGUIEnvironment* guienv;
      57             : 
      58             : /*
      59             :         QueuedMeshUpdate
      60             : */
      61             : 
      62        2139 : QueuedMeshUpdate::QueuedMeshUpdate():
      63             :         p(-1337,-1337,-1337),
      64             :         data(NULL),
      65        2139 :         ack_block_to_server(false)
      66             : {
      67        2139 : }
      68             : 
      69        4278 : QueuedMeshUpdate::~QueuedMeshUpdate()
      70             : {
      71        2139 :         if(data)
      72        2139 :                 delete data;
      73        2139 : }
      74             : 
      75             : /*
      76             :         MeshUpdateQueue
      77             : */
      78             : 
      79           1 : MeshUpdateQueue::MeshUpdateQueue()
      80             : {
      81           1 : }
      82             : 
      83           2 : MeshUpdateQueue::~MeshUpdateQueue()
      84             : {
      85           2 :         JMutexAutoLock lock(m_mutex);
      86             : 
      87           2 :         for(std::vector<QueuedMeshUpdate*>::iterator
      88           1 :                         i = m_queue.begin();
      89           2 :                         i != m_queue.end(); i++)
      90             :         {
      91           0 :                 QueuedMeshUpdate *q = *i;
      92           0 :                 delete q;
      93             :         }
      94           1 : }
      95             : 
      96             : /*
      97             :         peer_id=0 adds with nobody to send to
      98             : */
      99        3548 : void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
     100             : {
     101        5687 :         DSTACK(__FUNCTION_NAME);
     102             : 
     103             :         assert(data);   // pre-condition
     104             : 
     105        5687 :         JMutexAutoLock lock(m_mutex);
     106             : 
     107        3548 :         if(urgent)
     108         842 :                 m_urgents.insert(p);
     109             : 
     110             :         /*
     111             :                 Find if block is already in queue.
     112             :                 If it is, update the data and quit.
     113             :         */
     114      199573 :         for(std::vector<QueuedMeshUpdate*>::iterator
     115        3548 :                         i = m_queue.begin();
     116      135414 :                         i != m_queue.end(); i++)
     117             :         {
     118       65568 :                 QueuedMeshUpdate *q = *i;
     119       65568 :                 if(q->p == p)
     120             :                 {
     121        1409 :                         if(q->data)
     122        1409 :                                 delete q->data;
     123        1409 :                         q->data = data;
     124        1409 :                         if(ack_block_to_server)
     125          20 :                                 q->ack_block_to_server = true;
     126        2818 :                         return;
     127             :                 }
     128             :         }
     129             : 
     130             :         /*
     131             :                 Add the block
     132             :         */
     133        4278 :         QueuedMeshUpdate *q = new QueuedMeshUpdate;
     134        2139 :         q->p = p;
     135        2139 :         q->data = data;
     136        2139 :         q->ack_block_to_server = ack_block_to_server;
     137        2139 :         m_queue.push_back(q);
     138             : }
     139             : 
     140             : // Returned pointer must be deleted
     141             : // Returns NULL if queue is empty
     142        2156 : QueuedMeshUpdate *MeshUpdateQueue::pop()
     143             : {
     144        4312 :         JMutexAutoLock lock(m_mutex);
     145             : 
     146        2156 :         bool must_be_urgent = !m_urgents.empty();
     147       21274 :         for(std::vector<QueuedMeshUpdate*>::iterator
     148        2156 :                         i = m_queue.begin();
     149       15620 :                         i != m_queue.end(); i++)
     150             :         {
     151        7793 :                 QueuedMeshUpdate *q = *i;
     152        7793 :                 if(must_be_urgent && m_urgents.count(q->p) == 0)
     153        5654 :                         continue;
     154        2139 :                 m_queue.erase(i);
     155        2139 :                 m_urgents.erase(q->p);
     156        2139 :                 return q;
     157             :         }
     158          17 :         return NULL;
     159             : }
     160             : 
     161             : /*
     162             :         MeshUpdateThread
     163             : */
     164             : 
     165        3548 : void MeshUpdateThread::enqueueUpdate(v3s16 p, MeshMakeData *data,
     166             :                 bool ack_block_to_server, bool urgent)
     167             : {
     168        3548 :         m_queue_in.addBlock(p, data, ack_block_to_server, urgent);
     169        3548 :         deferUpdate();
     170        3548 : }
     171             : 
     172        2156 : void MeshUpdateThread::doUpdate()
     173             : {
     174             :         QueuedMeshUpdate *q;
     175        4295 :         while ((q = m_queue_in.pop())) {
     176             : 
     177        4278 :                 ScopeProfiler sp(g_profiler, "Client: Mesh making");
     178             : 
     179        2139 :                 MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
     180             : 
     181        2139 :                 MeshUpdateResult r;
     182        2139 :                 r.p = q->p;
     183        2139 :                 r.mesh = mesh_new;
     184        2139 :                 r.ack_block_to_server = q->ack_block_to_server;
     185             : 
     186        2139 :                 m_queue_out.push_back(r);
     187             : 
     188        2139 :                 delete q;
     189             :         }
     190          17 : }
     191             : 
     192             : /*
     193             :         Client
     194             : */
     195             : 
     196           1 : Client::Client(
     197             :                 IrrlichtDevice *device,
     198             :                 const char *playername,
     199             :                 std::string password,
     200             :                 MapDrawControl &control,
     201             :                 IWritableTextureSource *tsrc,
     202             :                 IWritableShaderSource *shsrc,
     203             :                 IWritableItemDefManager *itemdef,
     204             :                 IWritableNodeDefManager *nodedef,
     205             :                 ISoundManager *sound,
     206             :                 MtEventManager *event,
     207             :                 bool ipv6
     208             : ):
     209             :         m_packetcounter_timer(0.0),
     210             :         m_connection_reinit_timer(0.1),
     211             :         m_avg_rtt_timer(0.0),
     212             :         m_playerpos_send_timer(0.0),
     213             :         m_ignore_damage_timer(0.0),
     214             :         m_tsrc(tsrc),
     215             :         m_shsrc(shsrc),
     216             :         m_itemdef(itemdef),
     217             :         m_nodedef(nodedef),
     218             :         m_sound(sound),
     219             :         m_event(event),
     220             :         m_mesh_update_thread(),
     221             :         m_env(
     222             :                 new ClientMap(this, this, control,
     223           1 :                         device->getSceneManager()->getRootSceneNode(),
     224           2 :                         device->getSceneManager(), 666),
     225           1 :                 device->getSceneManager(),
     226             :                 tsrc, this, device
     227             :         ),
     228             :         m_particle_manager(&m_env),
     229             :         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
     230             :         m_device(device),
     231             :         m_server_ser_ver(SER_FMT_VER_INVALID),
     232             :         m_playeritem(0),
     233             :         m_inventory_updated(false),
     234             :         m_inventory_from_server(NULL),
     235             :         m_inventory_from_server_age(0.0),
     236             :         m_show_highlighted(false),
     237             :         m_animation_time(0),
     238             :         m_crack_level(-1),
     239             :         m_crack_pos(0,0,0),
     240             :         m_highlighted_pos(0,0,0),
     241             :         m_map_seed(0),
     242             :         m_password(password),
     243             :         m_chosen_auth_mech(AUTH_MECHANISM_NONE),
     244             :         m_auth_data(NULL),
     245             :         m_access_denied(false),
     246             :         m_itemdef_received(false),
     247             :         m_nodedef_received(false),
     248           0 :         m_media_downloader(new ClientMediaDownloader()),
     249             :         m_time_of_day_set(false),
     250             :         m_last_time_of_day_f(-1),
     251             :         m_time_of_day_update_timer(0),
     252             :         m_recommended_send_interval(0.1),
     253             :         m_removed_sounds_check_timer(0),
     254             :         m_state(LC_Created),
     255           5 :         m_localdb(NULL)
     256             : {
     257             :         // Add local player
     258           1 :         m_env.addPlayer(new LocalPlayer(this, playername));
     259             : 
     260           1 :         m_mapper = new Mapper(device, this);
     261           1 :         m_cache_save_interval = g_settings->getU16("server_map_save_interval");
     262             : 
     263           1 :         m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
     264           1 :         m_cache_enable_shaders  = g_settings->getBool("enable_shaders");
     265           1 : }
     266             : 
     267           1 : void Client::Stop()
     268             : {
     269             :         //request all client managed threads to stop
     270           1 :         m_mesh_update_thread.Stop();
     271             :         // Save local server map
     272           1 :         if (m_localdb) {
     273           0 :                 infostream << "Local map saving ended." << std::endl;
     274           0 :                 m_localdb->endSave();
     275             :         }
     276           1 : }
     277             : 
     278           3 : bool Client::isShutdown()
     279             : {
     280             : 
     281           3 :         if (!m_mesh_update_thread.IsRunning()) return true;
     282             : 
     283           2 :         return false;
     284             : }
     285             : 
     286           3 : Client::~Client()
     287             : {
     288           1 :         m_con.Disconnect();
     289             : 
     290           1 :         m_mesh_update_thread.Stop();
     291           1 :         m_mesh_update_thread.Wait();
     292          31 :         while(!m_mesh_update_thread.m_queue_out.empty()) {
     293          15 :                 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
     294          15 :                 delete r.mesh;
     295             :         }
     296             : 
     297             : 
     298           1 :         delete m_inventory_from_server;
     299             : 
     300             :         // Delete detached inventories
     301          29 :         for(std::map<std::string, Inventory*>::iterator
     302           1 :                         i = m_detached_inventories.begin();
     303          20 :                         i != m_detached_inventories.end(); i++){
     304           9 :                 delete i->second;
     305             :         }
     306             : 
     307             :         // cleanup 3d model meshes on client shutdown
     308           1 :         while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
     309             :                 scene::IAnimatedMesh * mesh =
     310           0 :                         m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
     311             : 
     312           0 :                 if (mesh != NULL)
     313           0 :                         m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
     314             :         }
     315           2 : }
     316             : 
     317           1 : void Client::connect(Address address,
     318             :                 const std::string &address_name,
     319             :                 bool is_local_server)
     320             : {
     321           2 :         DSTACK(__FUNCTION_NAME);
     322             : 
     323           1 :         initLocalMapSaving(address, address_name, is_local_server);
     324             : 
     325           1 :         m_con.SetTimeoutMs(0);
     326           1 :         m_con.Connect(address);
     327           1 : }
     328             : 
     329        1193 : void Client::step(float dtime)
     330             : {
     331        2379 :         DSTACK(__FUNCTION_NAME);
     332             : 
     333             :         // Limit a bit
     334        1193 :         if(dtime > 2.0)
     335           1 :                 dtime = 2.0;
     336             : 
     337        1193 :         if(m_ignore_damage_timer > dtime)
     338         169 :                 m_ignore_damage_timer -= dtime;
     339             :         else
     340        1024 :                 m_ignore_damage_timer = 0.0;
     341             : 
     342        1193 :         m_animation_time += dtime;
     343        1193 :         if(m_animation_time > 60.0)
     344           0 :                 m_animation_time -= 60.0;
     345             : 
     346        1193 :         m_time_of_day_update_timer += dtime;
     347             : 
     348        1193 :         ReceiveAll();
     349             : 
     350             :         /*
     351             :                 Packet counter
     352             :         */
     353             :         {
     354        1193 :                 float &counter = m_packetcounter_timer;
     355        1193 :                 counter -= dtime;
     356        1193 :                 if(counter <= 0.0)
     357             :                 {
     358           2 :                         counter = 20.0;
     359             : 
     360           2 :                         infostream << "Client packetcounter (" << m_packetcounter_timer
     361           2 :                                         << "):"<<std::endl;
     362           2 :                         m_packetcounter.print(infostream);
     363           2 :                         m_packetcounter.clear();
     364             :                 }
     365             :         }
     366             : 
     367             :         // UGLY hack to fix 2 second startup delay caused by non existent
     368             :         // server client startup synchronization in local server or singleplayer mode
     369             :         static bool initial_step = true;
     370        1193 :         if (initial_step) {
     371           1 :                 initial_step = false;
     372             :         }
     373        1192 :         else if(m_state == LC_Created) {
     374           7 :                 float &counter = m_connection_reinit_timer;
     375           7 :                 counter -= dtime;
     376           7 :                 if(counter <= 0.0) {
     377           1 :                         counter = 2.0;
     378             : 
     379           1 :                         Player *myplayer = m_env.getLocalPlayer();
     380           1 :                         FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");
     381             : 
     382             :                         // Send TOSERVER_INIT_LEGACY
     383             :                         // [0] u16 TOSERVER_INIT_LEGACY
     384             :                         // [2] u8 SER_FMT_VER_HIGHEST_READ
     385             :                         // [3] u8[20] player_name
     386             :                         // [23] u8[28] password (new in some version)
     387             :                         // [51] u16 minimum supported network protocol version (added sometime)
     388             :                         // [53] u16 maximum supported network protocol version (added later than the previous one)
     389             : 
     390             :                         char pName[PLAYERNAME_SIZE];
     391             :                         char pPassword[PASSWORD_SIZE];
     392           1 :                         memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
     393           1 :                         memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
     394             : 
     395           2 :                         std::string hashed_password = translatePassword(myplayer->getName(), m_password);
     396           1 :                         snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
     397           1 :                         snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
     398             : 
     399           1 :                         sendLegacyInit(pName, pPassword);
     400             :                         if (LATEST_PROTOCOL_VERSION >= 25)
     401           1 :                                 sendInit(myplayer->getName());
     402             :                 }
     403             : 
     404             :                 // Not connected, return
     405           7 :                 return;
     406             :         }
     407             : 
     408             :         /*
     409             :                 Do stuff if connected
     410             :         */
     411             : 
     412             :         /*
     413             :                 Run Map's timers and unload unused data
     414             :         */
     415        1186 :         const float map_timer_and_unload_dtime = 5.25;
     416        1186 :         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
     417          12 :                 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
     418          12 :                 std::vector<v3s16> deleted_blocks;
     419          12 :                 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
     420             :                                 g_settings->getFloat("client_unload_unused_data_timeout"),
     421           6 :                                 &deleted_blocks);
     422             : 
     423             :                 /*
     424             :                         Send info to server
     425             :                         NOTE: This loop is intentionally iterated the way it is.
     426             :                 */
     427             : 
     428           6 :                 std::vector<v3s16>::iterator i = deleted_blocks.begin();
     429          12 :                 std::vector<v3s16> sendlist;
     430           0 :                 for(;;) {
     431           6 :                         if(sendlist.size() == 255 || i == deleted_blocks.end()) {
     432           6 :                                 if(sendlist.empty())
     433           6 :                                         break;
     434             :                                 /*
     435             :                                         [0] u16 command
     436             :                                         [2] u8 count
     437             :                                         [3] v3s16 pos_0
     438             :                                         [3+6] v3s16 pos_1
     439             :                                         ...
     440             :                                 */
     441             : 
     442           0 :                                 sendDeletedBlocks(sendlist);
     443             : 
     444           0 :                                 if(i == deleted_blocks.end())
     445           0 :                                         break;
     446             : 
     447           0 :                                 sendlist.clear();
     448             :                         }
     449             : 
     450           0 :                         sendlist.push_back(*i);
     451           0 :                         ++i;
     452             :                 }
     453             :         }
     454             : 
     455             :         /*
     456             :                 Handle environment
     457             :         */
     458             :         // Control local player (0ms)
     459        1186 :         LocalPlayer *player = m_env.getLocalPlayer();
     460             :         assert(player != NULL);
     461        1186 :         player->applyControl(dtime);
     462             : 
     463             :         // Step environment
     464        1186 :         m_env.step(dtime);
     465             : 
     466             :         /*
     467             :                 Get events
     468             :         */
     469           0 :         for(;;) {
     470        1186 :                 ClientEnvEvent event = m_env.getClientEvent();
     471        1186 :                 if(event.type == CEE_NONE) {
     472        2372 :                         break;
     473             :                 }
     474           0 :                 else if(event.type == CEE_PLAYER_DAMAGE) {
     475           0 :                         if(m_ignore_damage_timer <= 0) {
     476           0 :                                 u8 damage = event.player_damage.amount;
     477             : 
     478           0 :                                 if(event.player_damage.send_to_server)
     479           0 :                                         sendDamage(damage);
     480             : 
     481             :                                 // Add to ClientEvent queue
     482             :                                 ClientEvent event;
     483           0 :                                 event.type = CE_PLAYER_DAMAGE;
     484           0 :                                 event.player_damage.amount = damage;
     485           0 :                                 m_client_event_queue.push(event);
     486             :                         }
     487             :                 }
     488           0 :                 else if(event.type == CEE_PLAYER_BREATH) {
     489           0 :                                 u16 breath = event.player_breath.amount;
     490           0 :                                 sendBreath(breath);
     491             :                 }
     492             :         }
     493             : 
     494             :         /*
     495             :                 Print some info
     496             :         */
     497        1186 :         float &counter = m_avg_rtt_timer;
     498        1186 :         counter += dtime;
     499        1186 :         if(counter >= 10) {
     500           3 :                 counter = 0.0;
     501             :                 // connectedAndInitialized() is true, peer exists.
     502           3 :                 float avg_rtt = getRTT();
     503           3 :                 infostream << "Client: avg_rtt=" << avg_rtt << std::endl;
     504             :         }
     505             : 
     506             :         /*
     507             :                 Send player position to server
     508             :         */
     509             :         {
     510        1186 :                 float &counter = m_playerpos_send_timer;
     511        1186 :                 counter += dtime;
     512        1186 :                 if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
     513             :                 {
     514         273 :                         counter = 0.0;
     515         273 :                         sendPlayerPos();
     516             :                 }
     517             :         }
     518             : 
     519             :         /*
     520             :                 Replace updated meshes
     521             :         */
     522             :         {
     523        1186 :                 int num_processed_meshes = 0;
     524        5434 :                 while (!m_mesh_update_thread.m_queue_out.empty())
     525             :                 {
     526        2124 :                         num_processed_meshes++;
     527        2124 :                         MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
     528        2124 :                         MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
     529        2124 :                         MinimapMapblock *minimap_mapblock = NULL;
     530        2124 :                         if (block) {
     531             :                                 // Delete the old mesh
     532        2124 :                                 if (block->mesh != NULL)     {
     533        1250 :                                         delete block->mesh;
     534        1250 :                                         block->mesh = NULL;
     535             :                                 }
     536             : 
     537        2124 :                                 if (r.mesh)
     538        2124 :                                         minimap_mapblock = r.mesh->getMinimapMapblock();
     539             : 
     540        2124 :                                 if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
     541         220 :                                         delete r.mesh;
     542         220 :                                         block->mesh = NULL;
     543             :                                 } else {
     544             :                                         // Replace with the new mesh
     545        1904 :                                         block->mesh = r.mesh;
     546             :                                 }
     547             :                         } else {
     548           0 :                                 delete r.mesh;
     549           0 :                                 minimap_mapblock = NULL;
     550             :                         }
     551             : 
     552        2124 :                         m_mapper->addBlock(r.p, minimap_mapblock);
     553             : 
     554        2124 :                         if (r.ack_block_to_server) {
     555             :                                 /*
     556             :                                         Acknowledge block
     557             :                                         [0] u8 count
     558             :                                         [1] v3s16 pos_0
     559             :                                 */
     560         768 :                                 sendGotBlocks(r.p);
     561             :                         }
     562             :                 }
     563             : 
     564        1186 :                 if (num_processed_meshes > 0)
     565        1040 :                         g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
     566             :         }
     567             : 
     568             :         /*
     569             :                 Load fetched media
     570             :         */
     571        1186 :         if (m_media_downloader && m_media_downloader->isStarted()) {
     572           1 :                 m_media_downloader->step(this);
     573           1 :                 if (m_media_downloader->isDone()) {
     574           1 :                         received_media();
     575           1 :                         delete m_media_downloader;
     576           1 :                         m_media_downloader = NULL;
     577             :                 }
     578             :         }
     579             : 
     580             :         /*
     581             :                 If the server didn't update the inventory in a while, revert
     582             :                 the local inventory (so the player notices the lag problem
     583             :                 and knows something is wrong).
     584             :         */
     585        1186 :         if(m_inventory_from_server)
     586             :         {
     587        1166 :                 float interval = 10.0;
     588        1166 :                 float count_before = floor(m_inventory_from_server_age / interval);
     589             : 
     590        1166 :                 m_inventory_from_server_age += dtime;
     591             : 
     592        1166 :                 float count_after = floor(m_inventory_from_server_age / interval);
     593             : 
     594        1166 :                 if(count_after != count_before)
     595             :                 {
     596             :                         // Do this every <interval> seconds after TOCLIENT_INVENTORY
     597             :                         // Reset the locally changed inventory to the authoritative inventory
     598           3 :                         Player *player = m_env.getLocalPlayer();
     599           3 :                         player->inventory = *m_inventory_from_server;
     600           3 :                         m_inventory_updated = true;
     601             :                 }
     602             :         }
     603             : 
     604             :         /*
     605             :                 Update positions of sounds attached to objects
     606             :         */
     607             :         {
     608        2549 :                 for(std::map<int, u16>::iterator
     609        1186 :                                 i = m_sounds_to_objects.begin();
     610        2490 :                                 i != m_sounds_to_objects.end(); i++)
     611             :                 {
     612          59 :                         int client_id = i->first;
     613          59 :                         u16 object_id = i->second;
     614          59 :                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
     615          59 :                         if(!cao)
     616           0 :                                 continue;
     617          59 :                         v3f pos = cao->getPosition();
     618          59 :                         m_sound->updateSoundPosition(client_id, pos);
     619             :                 }
     620             :         }
     621             : 
     622             :         /*
     623             :                 Handle removed remotely initiated sounds
     624             :         */
     625        1186 :         m_removed_sounds_check_timer += dtime;
     626        1186 :         if(m_removed_sounds_check_timer >= 2.32) {
     627          14 :                 m_removed_sounds_check_timer = 0;
     628             :                 // Find removed sounds and clear references to them
     629          28 :                 std::vector<s32> removed_server_ids;
     630          34 :                 for(std::map<s32, int>::iterator
     631          14 :                                 i = m_sounds_server_to_client.begin();
     632          32 :                                 i != m_sounds_server_to_client.end();) {
     633           2 :                         s32 server_id = i->first;
     634           2 :                         int client_id = i->second;
     635           2 :                         i++;
     636           2 :                         if(!m_sound->soundExists(client_id)) {
     637           1 :                                 m_sounds_server_to_client.erase(server_id);
     638           1 :                                 m_sounds_client_to_server.erase(client_id);
     639           1 :                                 m_sounds_to_objects.erase(client_id);
     640           1 :                                 removed_server_ids.push_back(server_id);
     641             :                         }
     642             :                 }
     643             : 
     644             :                 // Sync to server
     645          14 :                 if(!removed_server_ids.empty()) {
     646           1 :                         sendRemovedSounds(removed_server_ids);
     647             :                 }
     648             :         }
     649             : 
     650             :         // Write server map
     651        1186 :         if (m_localdb && m_localdb_save_interval.step(dtime,
     652           0 :                         m_cache_save_interval)) {
     653           0 :                 m_localdb->endSave();
     654           0 :                 m_localdb->beginSave();
     655             :         }
     656             : }
     657             : 
     658        2635 : bool Client::loadMedia(const std::string &data, const std::string &filename)
     659             : {
     660             :         // Silly irrlicht's const-incorrectness
     661        5270 :         Buffer<char> data_rw(data.c_str(), data.size());
     662             : 
     663        5270 :         std::string name;
     664             : 
     665             :         const char *image_ext[] = {
     666             :                 ".png", ".jpg", ".bmp", ".tga",
     667             :                 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
     668             :                 NULL
     669        2635 :         };
     670        2635 :         name = removeStringEnd(filename, image_ext);
     671        2635 :         if(name != "")
     672             :         {
     673        2283 :                 verbosestream<<"Client: Attempting to load image "
     674        2283 :                 <<"file \""<<filename<<"\""<<std::endl;
     675             : 
     676        2283 :                 io::IFileSystem *irrfs = m_device->getFileSystem();
     677        2283 :                 video::IVideoDriver *vdrv = m_device->getVideoDriver();
     678             : 
     679             :                 // Create an irrlicht memory file
     680        6849 :                 io::IReadFile *rfile = irrfs->createMemoryReadFile(
     681        6849 :                                 *data_rw, data_rw.getSize(), "_tempreadfile");
     682             : 
     683        2283 :                 FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file.");
     684             : 
     685             :                 // Read image
     686        2283 :                 video::IImage *img = vdrv->createImageFromFile(rfile);
     687        2283 :                 if(!img){
     688           0 :                         errorstream<<"Client: Cannot create image from data of "
     689           0 :                                         <<"file \""<<filename<<"\""<<std::endl;
     690           0 :                         rfile->drop();
     691           0 :                         return false;
     692             :                 }
     693             :                 else {
     694        2283 :                         m_tsrc->insertSourceImage(filename, img);
     695        2283 :                         img->drop();
     696        2283 :                         rfile->drop();
     697        2283 :                         return true;
     698             :                 }
     699             :         }
     700             : 
     701             :         const char *sound_ext[] = {
     702             :                 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
     703             :                 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
     704             :                 ".ogg", NULL
     705         352 :         };
     706         352 :         name = removeStringEnd(filename, sound_ext);
     707         352 :         if(name != "")
     708             :         {
     709         150 :                 verbosestream<<"Client: Attempting to load sound "
     710         150 :                 <<"file \""<<filename<<"\""<<std::endl;
     711         150 :                 m_sound->loadSoundData(name, data);
     712         150 :                 return true;
     713             :         }
     714             : 
     715             :         const char *model_ext[] = {
     716             :                 ".x", ".b3d", ".md2", ".obj",
     717             :                 NULL
     718         202 :         };
     719         202 :         name = removeStringEnd(filename, model_ext);
     720         202 :         if(name != "")
     721             :         {
     722         202 :                 verbosestream<<"Client: Storing model into memory: "
     723         202 :                                 <<"\""<<filename<<"\""<<std::endl;
     724         202 :                 if(m_mesh_data.count(filename))
     725           0 :                         errorstream<<"Multiple models with name \""<<filename.c_str()
     726           0 :                                         <<"\" found; replacing previous model"<<std::endl;
     727         202 :                 m_mesh_data[filename] = data;
     728         202 :                 return true;
     729             :         }
     730             : 
     731           0 :         errorstream<<"Client: Don't know how to load file \""
     732           0 :                         <<filename<<"\""<<std::endl;
     733           0 :         return false;
     734             : }
     735             : 
     736             : // Virtual methods from con::PeerHandler
     737           1 : void Client::peerAdded(con::Peer *peer)
     738             : {
     739           1 :         infostream<<"Client::peerAdded(): peer->id="
     740           2 :                         <<peer->id<<std::endl;
     741           1 : }
     742           0 : void Client::deletingPeer(con::Peer *peer, bool timeout)
     743             : {
     744             :         infostream<<"Client::deletingPeer(): "
     745           0 :                         "Server Peer is getting deleted "
     746           0 :                         <<"(timeout="<<timeout<<")"<<std::endl;
     747           0 : }
     748             : 
     749             : /*
     750             :         u16 command
     751             :         u16 number of files requested
     752             :         for each file {
     753             :                 u16 length of name
     754             :                 string name
     755             :         }
     756             : */
     757           0 : void Client::request_media(const std::vector<std::string> &file_requests)
     758             : {
     759           0 :         std::ostringstream os(std::ios_base::binary);
     760           0 :         writeU16(os, TOSERVER_REQUEST_MEDIA);
     761           0 :         size_t file_requests_size = file_requests.size();
     762             : 
     763           0 :         FATAL_ERROR_IF(file_requests_size > 0xFFFF, "Unsupported number of file requests");
     764             : 
     765             :         // Packet dynamicly resized
     766           0 :         NetworkPacket pkt(TOSERVER_REQUEST_MEDIA, 2 + 0);
     767             : 
     768           0 :         pkt << (u16) (file_requests_size & 0xFFFF);
     769             : 
     770           0 :         for(std::vector<std::string>::const_iterator i = file_requests.begin();
     771           0 :                         i != file_requests.end(); ++i) {
     772           0 :                 pkt << (*i);
     773             :         }
     774             : 
     775           0 :         Send(&pkt);
     776             : 
     777           0 :         infostream << "Client: Sending media request list to server ("
     778           0 :                         << file_requests.size() << " files. packet size)" << std::endl;
     779           0 : }
     780             : 
     781           1 : void Client::received_media()
     782             : {
     783           2 :         NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0);
     784           1 :         Send(&pkt);
     785           1 :         infostream << "Client: Notifying server that we received all media"
     786           1 :                         << std::endl;
     787           1 : }
     788             : 
     789           1 : void Client::initLocalMapSaving(const Address &address,
     790             :                 const std::string &hostname,
     791             :                 bool is_local_server)
     792             : {
     793           1 :         if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
     794           1 :                 return;
     795             :         }
     796             : 
     797             :         const std::string world_path = porting::path_user
     798           0 :                 + DIR_DELIM + "worlds"
     799           0 :                 + DIR_DELIM + "server_"
     800           0 :                 + hostname + "_" + to_string(address.getPort());
     801             : 
     802           0 :         fs::CreateAllDirs(world_path);
     803             : 
     804           0 :         m_localdb = new Database_SQLite3(world_path);
     805           0 :         m_localdb->beginSave();
     806           0 :         actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
     807             : }
     808             : 
     809        1193 : void Client::ReceiveAll()
     810             : {
     811        2386 :         DSTACK(__FUNCTION_NAME);
     812        1193 :         u32 start_ms = porting::getTimeMs();
     813        1321 :         for(;;)
     814             :         {
     815             :                 // Limit time even if there would be huge amounts of data to
     816             :                 // process
     817        2514 :                 if(porting::getTimeMs() > start_ms + 100)
     818           3 :                         break;
     819             : 
     820             :                 try {
     821        2511 :                         Receive();
     822        1321 :                         g_profiler->graphAdd("client_received_packets", 1);
     823             :                 }
     824        2380 :                 catch(con::NoIncomingDataException &e) {
     825        1190 :                         break;
     826             :                 }
     827           0 :                 catch(con::InvalidIncomingDataException &e) {
     828             :                         infostream<<"Client::ReceiveAll(): "
     829           0 :                                         "InvalidIncomingDataException: what()="
     830           0 :                                         <<e.what()<<std::endl;
     831             :                 }
     832             :         }
     833        1193 : }
     834             : 
     835        2511 : void Client::Receive()
     836             : {
     837        5022 :         DSTACK(__FUNCTION_NAME);
     838        5022 :         NetworkPacket pkt;
     839        2511 :         m_con.Receive(&pkt);
     840        1321 :         ProcessData(&pkt);
     841        1321 : }
     842             : 
     843        1321 : inline void Client::handleCommand(NetworkPacket* pkt)
     844             : {
     845        1321 :         const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
     846        1321 :         (this->*opHandle.handler)(pkt);
     847        1321 : }
     848             : 
     849             : /*
     850             :         sender_peer_id given to this shall be quaranteed to be a valid peer
     851             : */
     852        1321 : void Client::ProcessData(NetworkPacket *pkt)
     853             : {
     854        2641 :         DSTACK(__FUNCTION_NAME);
     855             : 
     856        1321 :         ToClientCommand command = (ToClientCommand) pkt->getCommand();
     857        1321 :         u32 sender_peer_id = pkt->getPeerId();
     858             : 
     859             :         //infostream<<"Client: received command="<<command<<std::endl;
     860        1321 :         m_packetcounter.add((u16)command);
     861             : 
     862             :         /*
     863             :                 If this check is removed, be sure to change the queue
     864             :                 system to know the ids
     865             :         */
     866        1321 :         if(sender_peer_id != PEER_ID_SERVER) {
     867             :                 infostream << "Client::ProcessData(): Discarding data not "
     868           0 :                         "coming from server: peer_id=" << sender_peer_id
     869           0 :                         << std::endl;
     870           0 :                 return;
     871             :         }
     872             : 
     873             :         // Command must be handled into ToClientCommandHandler
     874        1321 :         if (command >= TOCLIENT_NUM_MSG_TYPES) {
     875           0 :                 infostream << "Client: Ignoring unknown command "
     876           0 :                         << command << std::endl;
     877           0 :                 return;
     878             :         }
     879             : 
     880             :         /*
     881             :          * Those packets are handled before m_server_ser_ver is set, it's normal
     882             :          * But we must use the new ToClientConnectionState in the future,
     883             :          * as a byte mask
     884             :          */
     885        1321 :         if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
     886           1 :                 handleCommand(pkt);
     887           1 :                 return;
     888             :         }
     889             : 
     890        1320 :         if(m_server_ser_ver == SER_FMT_VER_INVALID) {
     891             :                 infostream << "Client: Server serialization"
     892             :                                 " format invalid or not initialized."
     893           0 :                                 " Skipping incoming command=" << command << std::endl;
     894           0 :                 return;
     895             :         }
     896             : 
     897             :         /*
     898             :           Handle runtime commands
     899             :         */
     900             : 
     901        1320 :         handleCommand(pkt);
     902             : }
     903             : 
     904         991 : void Client::Send(NetworkPacket* pkt)
     905             : {
     906        1982 :         m_con.Send(PEER_ID_SERVER,
     907         991 :                 serverCommandFactoryTable[pkt->getCommand()].channel,
     908             :                 pkt,
     909        1982 :                 serverCommandFactoryTable[pkt->getCommand()].reliable);
     910         991 : }
     911             : 
     912           0 : void Client::interact(u8 action, const PointedThing& pointed)
     913             : {
     914           0 :         if(m_state != LC_Ready) {
     915             :                 errorstream << "Client::interact() "
     916           0 :                                 "Canceled (not connected)"
     917           0 :                                 << std::endl;
     918           0 :                 return;
     919             :         }
     920             : 
     921             :         /*
     922             :                 [0] u16 command
     923             :                 [2] u8 action
     924             :                 [3] u16 item
     925             :                 [5] u32 length of the next item
     926             :                 [9] serialized PointedThing
     927             :                 actions:
     928             :                 0: start digging (from undersurface) or use
     929             :                 1: stop digging (all parameters ignored)
     930             :                 2: digging completed
     931             :                 3: place block or item (to abovesurface)
     932             :                 4: use item
     933             :         */
     934             : 
     935           0 :         NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0);
     936             : 
     937           0 :         pkt << action;
     938           0 :         pkt << (u16)getPlayerItem();
     939             : 
     940           0 :         std::ostringstream tmp_os(std::ios::binary);
     941           0 :         pointed.serialize(tmp_os);
     942             : 
     943           0 :         pkt.putLongString(tmp_os.str());
     944             : 
     945           0 :         Send(&pkt);
     946             : }
     947             : 
     948           0 : void Client::deleteAuthData()
     949             : {
     950           0 :         if (!m_auth_data)
     951           0 :                 return;
     952             : 
     953           0 :         switch (m_chosen_auth_mech) {
     954             :                 case AUTH_MECHANISM_FIRST_SRP:
     955           0 :                         break;
     956             :                 case AUTH_MECHANISM_SRP:
     957             :                 case AUTH_MECHANISM_LEGACY_PASSWORD:
     958           0 :                         srp_user_delete((SRPUser *) m_auth_data);
     959           0 :                         m_auth_data = NULL;
     960           0 :                         break;
     961             :                 case AUTH_MECHANISM_NONE:
     962           0 :                         break;
     963             :         }
     964             : }
     965             : 
     966             : 
     967           0 : AuthMechanism Client::choseAuthMech(const u32 mechs)
     968             : {
     969           0 :         if (mechs & AUTH_MECHANISM_SRP)
     970           0 :                 return AUTH_MECHANISM_SRP;
     971             : 
     972           0 :         if (mechs & AUTH_MECHANISM_FIRST_SRP)
     973           0 :                 return AUTH_MECHANISM_FIRST_SRP;
     974             : 
     975           0 :         if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
     976           0 :                 return AUTH_MECHANISM_LEGACY_PASSWORD;
     977             : 
     978           0 :         return AUTH_MECHANISM_NONE;
     979             : }
     980             : 
     981           1 : void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
     982             : {
     983             :         NetworkPacket pkt(TOSERVER_INIT_LEGACY,
     984           2 :                         1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2);
     985             : 
     986           1 :         pkt << (u8) SER_FMT_VER_HIGHEST_READ;
     987           1 :         pkt.putRawString(playerName,PLAYERNAME_SIZE);
     988           1 :         pkt.putRawString(playerPassword, PASSWORD_SIZE);
     989           1 :         pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
     990             : 
     991           1 :         Send(&pkt);
     992           1 : }
     993             : 
     994           1 : void Client::sendInit(const std::string &playerName)
     995             : {
     996           2 :         NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));
     997             : 
     998             :         // we don't support network compression yet
     999           1 :         u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE;
    1000           1 :         pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes;
    1001           1 :         pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
    1002           1 :         pkt << playerName;
    1003             : 
    1004           1 :         Send(&pkt);
    1005           1 : }
    1006             : 
    1007           0 : void Client::startAuth(AuthMechanism chosen_auth_mechanism)
    1008             : {
    1009           0 :         m_chosen_auth_mech = chosen_auth_mechanism;
    1010             : 
    1011           0 :         switch (chosen_auth_mechanism) {
    1012             :                 case AUTH_MECHANISM_FIRST_SRP: {
    1013             :                         // send srp verifier to server
    1014           0 :                         NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
    1015             :                         char *salt, *bytes_v;
    1016             :                         std::size_t len_salt, len_v;
    1017           0 :                         salt = NULL;
    1018           0 :                         getSRPVerifier(getPlayerName(), m_password,
    1019           0 :                                 &salt, &len_salt, &bytes_v, &len_v);
    1020             :                         resp_pkt
    1021           0 :                                 << std::string((char*)salt, len_salt)
    1022           0 :                                 << std::string((char*)bytes_v, len_v)
    1023           0 :                                 << (u8)((m_password == "") ? 1 : 0);
    1024           0 :                         free(salt);
    1025           0 :                         free(bytes_v);
    1026           0 :                         Send(&resp_pkt);
    1027           0 :                         break;
    1028             :                 }
    1029             :                 case AUTH_MECHANISM_SRP:
    1030             :                 case AUTH_MECHANISM_LEGACY_PASSWORD: {
    1031           0 :                         u8 based_on = 1;
    1032             : 
    1033           0 :                         if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
    1034           0 :                                 m_password = translatePassword(getPlayerName(), m_password);
    1035           0 :                                 based_on = 0;
    1036             :                         }
    1037             : 
    1038           0 :                         std::string playername_u = lowercase(getPlayerName());
    1039           0 :                         m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
    1040           0 :                                 getPlayerName().c_str(), playername_u.c_str(),
    1041           0 :                                 (const unsigned char *) m_password.c_str(),
    1042           0 :                                 m_password.length(), NULL, NULL);
    1043           0 :                         char *bytes_A = 0;
    1044           0 :                         size_t len_A = 0;
    1045           0 :                         srp_user_start_authentication((struct SRPUser *) m_auth_data,
    1046           0 :                                 NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);
    1047             : 
    1048           0 :                         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
    1049           0 :                         resp_pkt << std::string(bytes_A, len_A) << based_on;
    1050           0 :                         free(bytes_A);
    1051           0 :                         Send(&resp_pkt);
    1052           0 :                         break;
    1053             :                 }
    1054             :                 case AUTH_MECHANISM_NONE:
    1055           0 :                         break; // not handled in this method
    1056             :         }
    1057           0 : }
    1058             : 
    1059           0 : void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
    1060             : {
    1061           0 :         NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
    1062             : 
    1063           0 :         pkt << (u8) blocks.size();
    1064             : 
    1065           0 :         u32 k = 0;
    1066           0 :         for(std::vector<v3s16>::iterator
    1067           0 :                         j = blocks.begin();
    1068           0 :                         j != blocks.end(); ++j) {
    1069           0 :                 pkt << *j;
    1070           0 :                 k++;
    1071             :         }
    1072             : 
    1073           0 :         Send(&pkt);
    1074           0 : }
    1075             : 
    1076         768 : void Client::sendGotBlocks(v3s16 block)
    1077             : {
    1078        1536 :         NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6);
    1079         768 :         pkt << (u8) 1 << block;
    1080         768 :         Send(&pkt);
    1081         768 : }
    1082             : 
    1083           1 : void Client::sendRemovedSounds(std::vector<s32> &soundList)
    1084             : {
    1085           1 :         size_t server_ids = soundList.size();
    1086             :         assert(server_ids <= 0xFFFF);
    1087             : 
    1088           2 :         NetworkPacket pkt(TOSERVER_REMOVED_SOUNDS, 2 + server_ids * 4);
    1089             : 
    1090           1 :         pkt << (u16) (server_ids & 0xFFFF);
    1091             : 
    1092           6 :         for(std::vector<s32>::iterator i = soundList.begin();
    1093           4 :                         i != soundList.end(); i++)
    1094           1 :                 pkt << *i;
    1095             : 
    1096           1 :         Send(&pkt);
    1097           1 : }
    1098             : 
    1099           0 : void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
    1100             :                 const StringMap &fields)
    1101             : {
    1102           0 :         size_t fields_size = fields.size();
    1103             : 
    1104           0 :         FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of nodemeta fields");
    1105             : 
    1106           0 :         NetworkPacket pkt(TOSERVER_NODEMETA_FIELDS, 0);
    1107             : 
    1108           0 :         pkt << p << formname << (u16) (fields_size & 0xFFFF);
    1109             : 
    1110           0 :         StringMap::const_iterator it;
    1111           0 :         for (it = fields.begin(); it != fields.end(); ++it) {
    1112           0 :                 const std::string &name = it->first;
    1113           0 :                 const std::string &value = it->second;
    1114           0 :                 pkt << name;
    1115           0 :                 pkt.putLongString(value);
    1116             :         }
    1117             : 
    1118           0 :         Send(&pkt);
    1119           0 : }
    1120             : 
    1121           0 : void Client::sendInventoryFields(const std::string &formname,
    1122             :                 const StringMap &fields)
    1123             : {
    1124           0 :         size_t fields_size = fields.size();
    1125           0 :         FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of inventory fields");
    1126             : 
    1127           0 :         NetworkPacket pkt(TOSERVER_INVENTORY_FIELDS, 0);
    1128           0 :         pkt << formname << (u16) (fields_size & 0xFFFF);
    1129             : 
    1130           0 :         StringMap::const_iterator it;
    1131           0 :         for (it = fields.begin(); it != fields.end(); ++it) {
    1132           0 :                 const std::string &name  = it->first;
    1133           0 :                 const std::string &value = it->second;
    1134           0 :                 pkt << name;
    1135           0 :                 pkt.putLongString(value);
    1136             :         }
    1137             : 
    1138           0 :         Send(&pkt);
    1139           0 : }
    1140             : 
    1141           0 : void Client::sendInventoryAction(InventoryAction *a)
    1142             : {
    1143           0 :         std::ostringstream os(std::ios_base::binary);
    1144             : 
    1145           0 :         a->serialize(os);
    1146             : 
    1147             :         // Make data buffer
    1148           0 :         std::string s = os.str();
    1149             : 
    1150           0 :         NetworkPacket pkt(TOSERVER_INVENTORY_ACTION, s.size());
    1151           0 :         pkt.putRawString(s.c_str(),s.size());
    1152             : 
    1153           0 :         Send(&pkt);
    1154           0 : }
    1155             : 
    1156           0 : void Client::sendChatMessage(const std::wstring &message)
    1157             : {
    1158           0 :         NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
    1159             : 
    1160           0 :         pkt << message;
    1161             : 
    1162           0 :         Send(&pkt);
    1163           0 : }
    1164             : 
    1165           0 : void Client::sendChangePassword(const std::string &oldpassword,
    1166             :         const std::string &newpassword)
    1167             : {
    1168           0 :         Player *player = m_env.getLocalPlayer();
    1169           0 :         if (player == NULL)
    1170           0 :                 return;
    1171             : 
    1172           0 :         std::string playername = player->getName();
    1173           0 :         if (m_proto_ver >= 25) {
    1174             :                 // get into sudo mode and then send new password to server
    1175           0 :                 m_password = oldpassword;
    1176           0 :                 m_new_password = newpassword;
    1177           0 :                 startAuth(choseAuthMech(m_sudo_auth_methods));
    1178             :         } else {
    1179           0 :                 std::string oldpwd = translatePassword(playername, oldpassword);
    1180           0 :                 std::string newpwd = translatePassword(playername, newpassword);
    1181             : 
    1182           0 :                 NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
    1183             : 
    1184           0 :                 for (u8 i = 0; i < PASSWORD_SIZE; i++) {
    1185           0 :                         pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
    1186             :                 }
    1187             : 
    1188           0 :                 for (u8 i = 0; i < PASSWORD_SIZE; i++) {
    1189           0 :                         pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
    1190             :                 }
    1191           0 :                 Send(&pkt);
    1192             :         }
    1193             : }
    1194             : 
    1195             : 
    1196           0 : void Client::sendDamage(u8 damage)
    1197             : {
    1198           0 :         DSTACK(__FUNCTION_NAME);
    1199             : 
    1200           0 :         NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
    1201           0 :         pkt << damage;
    1202           0 :         Send(&pkt);
    1203           0 : }
    1204             : 
    1205           0 : void Client::sendBreath(u16 breath)
    1206             : {
    1207           0 :         DSTACK(__FUNCTION_NAME);
    1208             : 
    1209           0 :         NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
    1210           0 :         pkt << breath;
    1211           0 :         Send(&pkt);
    1212           0 : }
    1213             : 
    1214           0 : void Client::sendRespawn()
    1215             : {
    1216           0 :         DSTACK(__FUNCTION_NAME);
    1217             : 
    1218           0 :         NetworkPacket pkt(TOSERVER_RESPAWN, 0);
    1219           0 :         Send(&pkt);
    1220           0 : }
    1221             : 
    1222           1 : void Client::sendReady()
    1223             : {
    1224           2 :         DSTACK(__FUNCTION_NAME);
    1225             : 
    1226             :         NetworkPacket pkt(TOSERVER_CLIENT_READY,
    1227           2 :                         1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash));
    1228             : 
    1229           1 :         pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH
    1230           2 :                 << (u8) 0 << (u16) strlen(g_version_hash);
    1231             : 
    1232           1 :         pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash));
    1233           1 :         Send(&pkt);
    1234           1 : }
    1235             : 
    1236         273 : void Client::sendPlayerPos()
    1237             : {
    1238         273 :         LocalPlayer *myplayer = m_env.getLocalPlayer();
    1239         273 :         if(myplayer == NULL)
    1240          76 :                 return;
    1241             : 
    1242             :         // Save bandwidth by only updating position when something changed
    1243        1344 :         if(myplayer->last_position        == myplayer->getPosition() &&
    1244         651 :                         myplayer->last_speed      == myplayer->getSpeed()    &&
    1245         206 :                         myplayer->last_pitch      == myplayer->getPitch()    &&
    1246         975 :                         myplayer->last_yaw        == myplayer->getYaw()      &&
    1247          76 :                         myplayer->last_keyPressed == myplayer->keyPressed)
    1248          76 :                 return;
    1249             : 
    1250         197 :         myplayer->last_position   = myplayer->getPosition();
    1251         197 :         myplayer->last_speed      = myplayer->getSpeed();
    1252         197 :         myplayer->last_pitch      = myplayer->getPitch();
    1253         197 :         myplayer->last_yaw        = myplayer->getYaw();
    1254         197 :         myplayer->last_keyPressed = myplayer->keyPressed;
    1255             : 
    1256             :         u16 our_peer_id;
    1257             :         {
    1258             :                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
    1259         197 :                 our_peer_id = m_con.GetPeerID();
    1260             :         }
    1261             : 
    1262             :         // Set peer id if not set already
    1263         197 :         if(myplayer->peer_id == PEER_ID_INEXISTENT)
    1264           1 :                 myplayer->peer_id = our_peer_id;
    1265             : 
    1266             :         assert(myplayer->peer_id == our_peer_id);
    1267             : 
    1268         197 :         v3f pf         = myplayer->getPosition();
    1269         197 :         v3f sf         = myplayer->getSpeed();
    1270         197 :         s32 pitch      = myplayer->getPitch() * 100;
    1271         197 :         s32 yaw        = myplayer->getYaw() * 100;
    1272         197 :         u32 keyPressed = myplayer->keyPressed;
    1273             : 
    1274         197 :         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
    1275         197 :         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
    1276             :         /*
    1277             :                 Format:
    1278             :                 [0] v3s32 position*100
    1279             :                 [12] v3s32 speed*100
    1280             :                 [12+12] s32 pitch*100
    1281             :                 [12+12+4] s32 yaw*100
    1282             :                 [12+12+4+4] u32 keyPressed
    1283             :         */
    1284             : 
    1285         394 :         NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4);
    1286             : 
    1287         197 :         pkt << position << speed << pitch << yaw << keyPressed;
    1288             : 
    1289         197 :         Send(&pkt);
    1290             : }
    1291             : 
    1292          20 : void Client::sendPlayerItem(u16 item)
    1293             : {
    1294          20 :         Player *myplayer = m_env.getLocalPlayer();
    1295          20 :         if(myplayer == NULL)
    1296           0 :                 return;
    1297             : 
    1298          20 :         u16 our_peer_id = m_con.GetPeerID();
    1299             : 
    1300             :         // Set peer id if not set already
    1301          20 :         if(myplayer->peer_id == PEER_ID_INEXISTENT)
    1302           0 :                 myplayer->peer_id = our_peer_id;
    1303             :         assert(myplayer->peer_id == our_peer_id);
    1304             : 
    1305          40 :         NetworkPacket pkt(TOSERVER_PLAYERITEM, 2);
    1306             : 
    1307          20 :         pkt << item;
    1308             : 
    1309          20 :         Send(&pkt);
    1310             : }
    1311             : 
    1312           6 : void Client::removeNode(v3s16 p)
    1313             : {
    1314          12 :         std::map<v3s16, MapBlock*> modified_blocks;
    1315             : 
    1316             :         try {
    1317           6 :                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
    1318             :         }
    1319           0 :         catch(InvalidPositionException &e) {
    1320             :         }
    1321             : 
    1322          45 :         for(std::map<v3s16, MapBlock *>::iterator
    1323           6 :                         i = modified_blocks.begin();
    1324          34 :                         i != modified_blocks.end(); ++i) {
    1325          11 :                 addUpdateMeshTaskWithEdge(i->first, false, true);
    1326             :         }
    1327           6 : }
    1328             : 
    1329          49 : void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
    1330             : {
    1331             :         //TimeTaker timer1("Client::addNode()");
    1332             : 
    1333          98 :         std::map<v3s16, MapBlock*> modified_blocks;
    1334             : 
    1335             :         try {
    1336             :                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
    1337          49 :                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
    1338             :         }
    1339           0 :         catch(InvalidPositionException &e) {
    1340             :         }
    1341             : 
    1342         389 :         for(std::map<v3s16, MapBlock *>::iterator
    1343          49 :                         i = modified_blocks.begin();
    1344         292 :                         i != modified_blocks.end(); ++i) {
    1345          97 :                 addUpdateMeshTaskWithEdge(i->first, false, true);
    1346             :         }
    1347          49 : }
    1348             : 
    1349        1166 : void Client::setPlayerControl(PlayerControl &control)
    1350             : {
    1351        1166 :         LocalPlayer *player = m_env.getLocalPlayer();
    1352             :         assert(player != NULL);
    1353        1166 :         player->control = control;
    1354        1166 : }
    1355             : 
    1356          20 : void Client::selectPlayerItem(u16 item)
    1357             : {
    1358          20 :         m_playeritem = item;
    1359          20 :         m_inventory_updated = true;
    1360          20 :         sendPlayerItem(item);
    1361          20 : }
    1362             : 
    1363             : // Returns true if the inventory of the local player has been
    1364             : // updated from the server. If it is true, it is set to false.
    1365        1166 : bool Client::getLocalInventoryUpdated()
    1366             : {
    1367        1166 :         bool updated = m_inventory_updated;
    1368        1166 :         m_inventory_updated = false;
    1369        1166 :         return updated;
    1370             : }
    1371             : 
    1372             : // Copies the inventory of the local player to parameter
    1373          24 : void Client::getLocalInventory(Inventory &dst)
    1374             : {
    1375          24 :         Player *player = m_env.getLocalPlayer();
    1376             :         assert(player != NULL);
    1377          24 :         dst = player->inventory;
    1378          24 : }
    1379             : 
    1380           0 : Inventory* Client::getInventory(const InventoryLocation &loc)
    1381             : {
    1382           0 :         switch(loc.type){
    1383             :         case InventoryLocation::UNDEFINED:
    1384             :         {}
    1385           0 :         break;
    1386             :         case InventoryLocation::CURRENT_PLAYER:
    1387             :         {
    1388           0 :                 Player *player = m_env.getLocalPlayer();
    1389             :                 assert(player != NULL);
    1390           0 :                 return &player->inventory;
    1391             :         }
    1392             :         break;
    1393             :         case InventoryLocation::PLAYER:
    1394             :         {
    1395           0 :                 Player *player = m_env.getPlayer(loc.name.c_str());
    1396           0 :                 if(!player)
    1397           0 :                         return NULL;
    1398           0 :                 return &player->inventory;
    1399             :         }
    1400             :         break;
    1401             :         case InventoryLocation::NODEMETA:
    1402             :         {
    1403           0 :                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
    1404           0 :                 if(!meta)
    1405           0 :                         return NULL;
    1406           0 :                 return meta->getInventory();
    1407             :         }
    1408             :         break;
    1409             :         case InventoryLocation::DETACHED:
    1410             :         {
    1411           0 :                 if(m_detached_inventories.count(loc.name) == 0)
    1412           0 :                         return NULL;
    1413           0 :                 return m_detached_inventories[loc.name];
    1414             :         }
    1415             :         break;
    1416             :         default:
    1417           0 :                 FATAL_ERROR("Invalid inventory location type.");
    1418             :                 break;
    1419             :         }
    1420           0 :         return NULL;
    1421             : }
    1422             : 
    1423           0 : void Client::inventoryAction(InventoryAction *a)
    1424             : {
    1425             :         /*
    1426             :                 Send it to the server
    1427             :         */
    1428           0 :         sendInventoryAction(a);
    1429             : 
    1430             :         /*
    1431             :                 Predict some local inventory changes
    1432             :         */
    1433           0 :         a->clientApply(this, this);
    1434             : 
    1435             :         // Remove it
    1436           0 :         delete a;
    1437           0 : }
    1438             : 
    1439        1166 : ClientActiveObject * Client::getSelectedActiveObject(
    1440             :                 f32 max_d,
    1441             :                 v3f from_pos_f_on_map,
    1442             :                 core::line3d<f32> shootline_on_map
    1443             :         )
    1444             : {
    1445        2332 :         std::vector<DistanceSortedActiveObject> objects;
    1446             : 
    1447        1166 :         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
    1448             : 
    1449             :         // Sort them.
    1450             :         // After this, the closest object is the first in the array.
    1451        1166 :         std::sort(objects.begin(), objects.end());
    1452             : 
    1453        2511 :         for(unsigned int i=0; i<objects.size(); i++)
    1454             :         {
    1455        1345 :                 ClientActiveObject *obj = objects[i].obj;
    1456             : 
    1457        1345 :                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
    1458        1345 :                 if(selection_box == NULL)
    1459        1153 :                         continue;
    1460             : 
    1461         192 :                 v3f pos = obj->getPosition();
    1462             : 
    1463             :                 core::aabbox3d<f32> offsetted_box(
    1464         384 :                                 selection_box->MinEdge + pos,
    1465         384 :                                 selection_box->MaxEdge + pos
    1466         192 :                 );
    1467             : 
    1468         192 :                 if(offsetted_box.intersectsWithLine(shootline_on_map))
    1469             :                 {
    1470           0 :                         return obj;
    1471             :                 }
    1472             :         }
    1473             : 
    1474        1166 :         return NULL;
    1475             : }
    1476             : 
    1477           0 : std::list<std::string> Client::getConnectedPlayerNames()
    1478             : {
    1479           0 :         return m_env.getPlayerNames();
    1480             : }
    1481             : 
    1482        2332 : float Client::getAnimationTime()
    1483             : {
    1484        2332 :         return m_animation_time;
    1485             : }
    1486             : 
    1487        2332 : int Client::getCrackLevel()
    1488             : {
    1489        2332 :         return m_crack_level;
    1490             : }
    1491             : 
    1492          57 : void Client::setHighlighted(v3s16 pos, bool show_highlighted)
    1493             : {
    1494          57 :         m_show_highlighted = show_highlighted;
    1495          57 :         v3s16 old_highlighted_pos = m_highlighted_pos;
    1496          57 :         m_highlighted_pos = pos;
    1497          57 :         addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
    1498          57 :         addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
    1499          57 : }
    1500             : 
    1501           0 : void Client::setCrack(int level, v3s16 pos)
    1502             : {
    1503           0 :         int old_crack_level = m_crack_level;
    1504           0 :         v3s16 old_crack_pos = m_crack_pos;
    1505             : 
    1506           0 :         m_crack_level = level;
    1507           0 :         m_crack_pos = pos;
    1508             : 
    1509           0 :         if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
    1510             :         {
    1511             :                 // remove old crack
    1512           0 :                 addUpdateMeshTaskForNode(old_crack_pos, false, true);
    1513             :         }
    1514           0 :         if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
    1515             :         {
    1516             :                 // add new crack
    1517           0 :                 addUpdateMeshTaskForNode(pos, false, true);
    1518             :         }
    1519           0 : }
    1520             : 
    1521           0 : u16 Client::getHP()
    1522             : {
    1523           0 :         Player *player = m_env.getLocalPlayer();
    1524             :         assert(player != NULL);
    1525           0 :         return player->hp;
    1526             : }
    1527             : 
    1528           0 : u16 Client::getBreath()
    1529             : {
    1530           0 :         Player *player = m_env.getLocalPlayer();
    1531             :         assert(player != NULL);
    1532           0 :         return player->getBreath();
    1533             : }
    1534             : 
    1535        1168 : bool Client::getChatMessage(std::wstring &message)
    1536             : {
    1537        1168 :         if(m_chat_queue.size() == 0)
    1538        1166 :                 return false;
    1539           2 :         message = m_chat_queue.front();
    1540           2 :         m_chat_queue.pop();
    1541           2 :         return true;
    1542             : }
    1543             : 
    1544           0 : void Client::typeChatMessage(const std::wstring &message)
    1545             : {
    1546             :         // Discard empty line
    1547           0 :         if(message == L"")
    1548           0 :                 return;
    1549             : 
    1550             :         // Send to others
    1551           0 :         sendChatMessage(message);
    1552             : 
    1553             :         // Show locally
    1554           0 :         if (message[0] == L'/')
    1555             :         {
    1556           0 :                 m_chat_queue.push((std::wstring)L"issued command: " + message);
    1557             :         }
    1558             :         else
    1559             :         {
    1560           0 :                 LocalPlayer *player = m_env.getLocalPlayer();
    1561             :                 assert(player != NULL);
    1562           0 :                 std::wstring name = narrow_to_wide(player->getName());
    1563           0 :                 m_chat_queue.push((std::wstring)L"<" + name + L"> " + message);
    1564             :         }
    1565             : }
    1566             : 
    1567        6504 : void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
    1568             : {
    1569        6504 :         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
    1570        6504 :         if(b == NULL)
    1571        2956 :                 return;
    1572             : 
    1573             :         /*
    1574             :                 Create a task to update the mesh of the block
    1575             :         */
    1576             : 
    1577        3548 :         MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
    1578             : 
    1579             :         {
    1580             :                 //TimeTaker timer("data fill");
    1581             :                 // Release: ~0ms
    1582             :                 // Debug: 1-6ms, avg=2ms
    1583        3548 :                 data->fill(b);
    1584        3548 :                 data->setCrack(m_crack_level, m_crack_pos);
    1585        3548 :                 data->setHighlighted(m_highlighted_pos, m_show_highlighted);
    1586        3548 :                 data->setSmoothLighting(m_cache_smooth_lighting);
    1587             :         }
    1588             : 
    1589             :         // Add task to queue
    1590        3548 :         m_mesh_update_thread.enqueueUpdate(p, data, ack_to_server, urgent);
    1591             : }
    1592             : 
    1593         894 : void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
    1594             : {
    1595             :         try{
    1596         894 :                 addUpdateMeshTask(blockpos, ack_to_server, urgent);
    1597             :         }
    1598           0 :         catch(InvalidPositionException &e){}
    1599             : 
    1600             :         // Leading edge
    1601        6258 :         for (int i=0;i<6;i++)
    1602             :         {
    1603             :                 try{
    1604        5364 :                         v3s16 p = blockpos + g_6dirs[i];
    1605        5364 :                         addUpdateMeshTask(p, false, urgent);
    1606             :                 }
    1607           0 :                 catch(InvalidPositionException &e){}
    1608             :         }
    1609         894 : }
    1610             : 
    1611         114 : void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
    1612             : {
    1613             :         {
    1614         114 :                 v3s16 p = nodepos;
    1615         114 :                 infostream<<"Client::addUpdateMeshTaskForNode(): "
    1616         228 :                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
    1617         114 :                                 <<std::endl;
    1618             :         }
    1619             : 
    1620         114 :         v3s16 blockpos          = getNodeBlockPos(nodepos);
    1621         114 :         v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
    1622             : 
    1623             :         try{
    1624         114 :                 addUpdateMeshTask(blockpos, ack_to_server, urgent);
    1625             :         }
    1626           0 :         catch(InvalidPositionException &e) {}
    1627             : 
    1628             :         // Leading edge
    1629         114 :         if(nodepos.X == blockpos_relative.X){
    1630             :                 try{
    1631          30 :                         v3s16 p = blockpos + v3s16(-1,0,0);
    1632          30 :                         addUpdateMeshTask(p, false, urgent);
    1633             :                 }
    1634           0 :                 catch(InvalidPositionException &e){}
    1635             :         }
    1636             : 
    1637         114 :         if(nodepos.Y == blockpos_relative.Y){
    1638             :                 try{
    1639          66 :                         v3s16 p = blockpos + v3s16(0,-1,0);
    1640          66 :                         addUpdateMeshTask(p, false, urgent);
    1641             :                 }
    1642           0 :                 catch(InvalidPositionException &e){}
    1643             :         }
    1644             : 
    1645         114 :         if(nodepos.Z == blockpos_relative.Z){
    1646             :                 try{
    1647          36 :                         v3s16 p = blockpos + v3s16(0,0,-1);
    1648          36 :                         addUpdateMeshTask(p, false, urgent);
    1649             :                 }
    1650           0 :                 catch(InvalidPositionException &e){}
    1651             :         }
    1652         114 : }
    1653             : 
    1654        1193 : ClientEvent Client::getClientEvent()
    1655             : {
    1656             :         ClientEvent event;
    1657        1193 :         if(m_client_event_queue.size() == 0) {
    1658        1166 :                 event.type = CE_NONE;
    1659             :         }
    1660             :         else {
    1661          27 :                 event = m_client_event_queue.front();
    1662          27 :                 m_client_event_queue.pop();
    1663             :         }
    1664        1193 :         return event;
    1665             : }
    1666             : 
    1667           1 : float Client::mediaReceiveProgress()
    1668             : {
    1669           1 :         if (m_media_downloader)
    1670           1 :                 return m_media_downloader->getProgress();
    1671             :         else
    1672           0 :                 return 1.0; // downloader only exists when not yet done
    1673             : }
    1674             : 
    1675             : typedef struct TextureUpdateArgs {
    1676             :         IrrlichtDevice *device;
    1677             :         gui::IGUIEnvironment *guienv;
    1678             :         u32 last_time_ms;
    1679             :         u16 last_percent;
    1680             :         const wchar_t* text_base;
    1681             : } TextureUpdateArgs;
    1682             : 
    1683        5195 : void texture_update_progress(void *args, u32 progress, u32 max_progress)
    1684             : {
    1685        5195 :                 TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
    1686        5195 :                 u16 cur_percent = ceil(progress / (double) max_progress * 100.);
    1687             : 
    1688             :                 // update the loading menu -- if neccessary
    1689        5195 :                 bool do_draw = false;
    1690        5195 :                 u32 time_ms = targs->last_time_ms;
    1691        5195 :                 if (cur_percent != targs->last_percent) {
    1692         100 :                         targs->last_percent = cur_percent;
    1693         100 :                         time_ms = getTimeMs();
    1694             :                         // only draw when the user will notice something:
    1695         100 :                         do_draw = (time_ms - targs->last_time_ms > 100);
    1696             :                 }
    1697             : 
    1698        5195 :                 if (do_draw) {
    1699          42 :                         targs->last_time_ms = time_ms;
    1700          84 :                         std::basic_stringstream<wchar_t> strm;
    1701          42 :                         strm << targs->text_base << " " << targs->last_percent << "%...";
    1702          84 :                         draw_load_screen(strm.str(), targs->device, targs->guienv, 0,
    1703          84 :                                 72 + (u16) ((18. / 100.) * (double) targs->last_percent));
    1704             :                 }
    1705        5195 : }
    1706             : 
    1707           1 : void Client::afterContentReceived(IrrlichtDevice *device)
    1708             : {
    1709           1 :         infostream<<"Client::afterContentReceived() started"<<std::endl;
    1710             :         assert(m_itemdef_received); // pre-condition
    1711             :         assert(m_nodedef_received); // pre-condition
    1712             :         assert(mediaReceived()); // pre-condition
    1713             : 
    1714           1 :         const wchar_t* text = wgettext("Loading textures...");
    1715             : 
    1716             :         // Clear cached pre-scaled 2D GUI images, as this cache
    1717             :         // might have images with the same name but different
    1718             :         // content from previous sessions.
    1719           1 :         guiScalingCacheClear(device->getVideoDriver());
    1720             : 
    1721             :         // Rebuild inherited images and recreate textures
    1722           1 :         infostream<<"- Rebuilding images and textures"<<std::endl;
    1723           1 :         draw_load_screen(text,device, guienv, 0, 70);
    1724           1 :         m_tsrc->rebuildImagesAndTextures();
    1725           1 :         delete[] text;
    1726             : 
    1727             :         // Rebuild shaders
    1728           1 :         infostream<<"- Rebuilding shaders"<<std::endl;
    1729           1 :         text = wgettext("Rebuilding shaders...");
    1730           1 :         draw_load_screen(text, device, guienv, 0, 71);
    1731           1 :         m_shsrc->rebuildShaders();
    1732           1 :         delete[] text;
    1733             : 
    1734             :         // Update node aliases
    1735           1 :         infostream<<"- Updating node aliases"<<std::endl;
    1736           1 :         text = wgettext("Initializing nodes...");
    1737           1 :         draw_load_screen(text, device, guienv, 0, 72);
    1738           1 :         m_nodedef->updateAliases(m_itemdef);
    1739           2 :         std::string texture_path = g_settings->get("texture_path");
    1740           1 :         if (texture_path != "" && fs::IsDir(texture_path))
    1741           1 :                 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
    1742           1 :         m_nodedef->setNodeRegistrationStatus(true);
    1743           1 :         m_nodedef->runNodeResolveCallbacks();
    1744           1 :         delete[] text;
    1745             : 
    1746             :         // Update node textures and assign shaders to each tile
    1747           1 :         infostream<<"- Updating node textures"<<std::endl;
    1748             :         TextureUpdateArgs tu_args;
    1749           1 :         tu_args.device = device;
    1750           1 :         tu_args.guienv = guienv;
    1751           1 :         tu_args.last_time_ms = getTimeMs();
    1752           1 :         tu_args.last_percent = 0;
    1753           1 :         tu_args.text_base =  wgettext("Initializing nodes");
    1754           1 :         m_nodedef->updateTextures(this, texture_update_progress, &tu_args);
    1755           1 :         delete[] tu_args.text_base;
    1756             : 
    1757             :         // Preload item textures and meshes if configured to
    1758           1 :         if(g_settings->getBool("preload_item_visuals"))
    1759             :         {
    1760           0 :                 verbosestream<<"Updating item textures and meshes"<<std::endl;
    1761           0 :                 text = wgettext("Item textures...");
    1762           0 :                 draw_load_screen(text, device, guienv, 0, 0);
    1763           0 :                 std::set<std::string> names = m_itemdef->getAll();
    1764           0 :                 size_t size = names.size();
    1765           0 :                 size_t count = 0;
    1766           0 :                 int percent = 0;
    1767           0 :                 for(std::set<std::string>::const_iterator
    1768           0 :                                 i = names.begin(); i != names.end(); ++i)
    1769             :                 {
    1770             :                         // Asking for these caches the result
    1771           0 :                         m_itemdef->getInventoryTexture(*i, this);
    1772           0 :                         m_itemdef->getWieldMesh(*i, this);
    1773           0 :                         count++;
    1774           0 :                         percent = (count * 100 / size * 0.2) + 80;
    1775           0 :                         draw_load_screen(text, device, guienv, 0, percent);
    1776             :                 }
    1777           0 :                 delete[] text;
    1778             :         }
    1779             : 
    1780             :         // Start mesh update thread after setting up content definitions
    1781           1 :         infostream<<"- Starting mesh update thread"<<std::endl;
    1782           1 :         m_mesh_update_thread.Start();
    1783             : 
    1784           1 :         m_state = LC_Ready;
    1785           1 :         sendReady();
    1786           1 :         text = wgettext("Done!");
    1787           1 :         draw_load_screen(text, device, guienv, 0, 100);
    1788           1 :         infostream<<"Client::afterContentReceived() done"<<std::endl;
    1789           1 :         delete[] text;
    1790           1 : }
    1791             : 
    1792           3 : float Client::getRTT(void)
    1793             : {
    1794           3 :         return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
    1795             : }
    1796             : 
    1797           0 : float Client::getCurRate(void)
    1798             : {
    1799           0 :         return ( m_con.getLocalStat(con::CUR_INC_RATE) +
    1800           0 :                         m_con.getLocalStat(con::CUR_DL_RATE));
    1801             : }
    1802             : 
    1803           0 : float Client::getAvgRate(void)
    1804             : {
    1805           0 :         return ( m_con.getLocalStat(con::AVG_INC_RATE) +
    1806           0 :                         m_con.getLocalStat(con::AVG_DL_RATE));
    1807             : }
    1808             : 
    1809           0 : void Client::makeScreenshot(IrrlichtDevice *device)
    1810             : {
    1811           0 :         irr::video::IVideoDriver *driver = device->getVideoDriver();
    1812           0 :         irr::video::IImage* const raw_image = driver->createScreenShot();
    1813             : 
    1814           0 :         if (!raw_image)
    1815           0 :                 return;
    1816             : 
    1817           0 :         time_t t = time(NULL);
    1818           0 :         struct tm *tm = localtime(&t);
    1819             : 
    1820             :         char timetstamp_c[64];
    1821           0 :         strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
    1822             : 
    1823           0 :         std::string filename_base = g_settings->get("screenshot_path")
    1824           0 :                         + DIR_DELIM
    1825           0 :                         + std::string("screenshot_")
    1826           0 :                         + std::string(timetstamp_c);
    1827           0 :         std::string filename_ext = ".png";
    1828           0 :         std::string filename;
    1829             : 
    1830             :         // Try to find a unique filename
    1831           0 :         unsigned serial = 0;
    1832             : 
    1833           0 :         while (serial < SCREENSHOT_MAX_SERIAL_TRIES) {
    1834           0 :                 filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext;
    1835           0 :                 std::ifstream tmp(filename.c_str());
    1836           0 :                 if (!tmp.good())
    1837           0 :                         break;  // File did not apparently exist, we'll go with it
    1838           0 :                 serial++;
    1839             :         }
    1840             : 
    1841           0 :         if (serial == SCREENSHOT_MAX_SERIAL_TRIES) {
    1842           0 :                 infostream << "Could not find suitable filename for screenshot" << std::endl;
    1843             :         } else {
    1844             :                 irr::video::IImage* const image =
    1845           0 :                                 driver->createImage(video::ECF_R8G8B8, raw_image->getDimension());
    1846             : 
    1847           0 :                 if (image) {
    1848           0 :                         raw_image->copyTo(image);
    1849             : 
    1850           0 :                         std::ostringstream sstr;
    1851           0 :                         if (driver->writeImageToFile(image, filename.c_str())) {
    1852           0 :                                 sstr << "Saved screenshot to '" << filename << "'";
    1853             :                         } else {
    1854           0 :                                 sstr << "Failed to save screenshot '" << filename << "'";
    1855             :                         }
    1856           0 :                         m_chat_queue.push(narrow_to_wide(sstr.str()));
    1857           0 :                         infostream << sstr.str() << std::endl;
    1858           0 :                         image->drop();
    1859             :                 }
    1860             :         }
    1861             : 
    1862           0 :         raw_image->drop();
    1863             : }
    1864             : 
    1865             : // IGameDef interface
    1866             : // Under envlock
    1867      203197 : IItemDefManager* Client::getItemDefManager()
    1868             : {
    1869      203197 :         return m_itemdef;
    1870             : }
    1871    33142539 : INodeDefManager* Client::getNodeDefManager()
    1872             : {
    1873    33142539 :         return m_nodedef;
    1874             : }
    1875           0 : ICraftDefManager* Client::getCraftDefManager()
    1876             : {
    1877           0 :         return NULL;
    1878             :         //return m_craftdef;
    1879             : }
    1880      952278 : ITextureSource* Client::getTextureSource()
    1881             : {
    1882      952278 :         return m_tsrc;
    1883             : }
    1884        2142 : IShaderSource* Client::getShaderSource()
    1885             : {
    1886        2142 :         return m_shsrc;
    1887             : }
    1888        4280 : scene::ISceneManager* Client::getSceneManager()
    1889             : {
    1890        4280 :         return m_device->getSceneManager();
    1891             : }
    1892           0 : u16 Client::allocateUnknownNodeId(const std::string &name)
    1893             : {
    1894           0 :         errorstream << "Client::allocateUnknownNodeId(): "
    1895           0 :                         << "Client cannot allocate node IDs" << std::endl;
    1896           0 :         FATAL_ERROR("Client allocated unknown node");
    1897             : 
    1898             :         return CONTENT_IGNORE;
    1899             : }
    1900          26 : ISoundManager* Client::getSoundManager()
    1901             : {
    1902          26 :         return m_sound;
    1903             : }
    1904          38 : MtEventManager* Client::getEventManager()
    1905             : {
    1906          38 :         return m_event;
    1907             : }
    1908             : 
    1909        1175 : ParticleManager* Client::getParticleManager()
    1910             : {
    1911        1175 :         return &m_particle_manager;
    1912             : }
    1913             : 
    1914        1429 : scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
    1915             : {
    1916        1429 :         StringMap::const_iterator it = m_mesh_data.find(filename);
    1917        1429 :         if (it == m_mesh_data.end()) {
    1918           0 :                 errorstream << "Client::getMesh(): Mesh not found: \"" << filename
    1919           0 :                         << "\"" << std::endl;
    1920           0 :                 return NULL;
    1921             :         }
    1922        1429 :         const std::string &data    = it->second;
    1923        1429 :         scene::ISceneManager *smgr = m_device->getSceneManager();
    1924             : 
    1925             :         // Create the mesh, remove it from cache and return it
    1926             :         // This allows unique vertex colors and other properties for each instance
    1927        2858 :         Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
    1928        1429 :         io::IFileSystem *irrfs = m_device->getFileSystem();
    1929        4287 :         io::IReadFile *rfile   = irrfs->createMemoryReadFile(
    1930        4287 :                         *data_rw, data_rw.getSize(), filename.c_str());
    1931        1429 :         FATAL_ERROR_IF(!rfile, "Could not create/open RAM file");
    1932             : 
    1933        1429 :         scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
    1934        1429 :         rfile->drop();
    1935             :         // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
    1936             :         // of uniquely named instances and re-use them
    1937        1429 :         mesh->grab();
    1938        1429 :         smgr->getMeshCache()->removeMesh(mesh);
    1939        1429 :         return mesh;
    1940           3 : }

Generated by: LCOV version 1.11