]> git.lizzy.rs Git - minetest.git/blob - src/client.cpp
Rewrite client media download and support hash-based remote download
[minetest.git] / src / client.cpp
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 "client.h"
21 #include <iostream>
22 #include <algorithm>
23 #include "clientserver.h"
24 #include "jthread/jmutexautolock.h"
25 #include "main.h"
26 #include <sstream>
27 #include "filesys.h"
28 #include "porting.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
31 #include "mapblock.h"
32 #include "settings.h"
33 #include "profiler.h"
34 #include "gettext.h"
35 #include "log.h"
36 #include "nodemetadata.h"
37 #include "nodedef.h"
38 #include "itemdef.h"
39 #include "shader.h"
40 #include <IFileSystem.h>
41 #include "base64.h"
42 #include "clientmap.h"
43 #include "clientmedia.h"
44 #include "sound.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
49 #include "config.h"
50 #include "util/directiontables.h"
51 #include "util/pointedthing.h"
52 #include "version.h"
53
54 #if USE_CURL
55 #include <curl/curl.h>
56 #endif
57
58 static std::string getMediaCacheDir()
59 {
60         return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
61 }
62
63 /*
64         QueuedMeshUpdate
65 */
66
67 QueuedMeshUpdate::QueuedMeshUpdate():
68         p(-1337,-1337,-1337),
69         data(NULL),
70         ack_block_to_server(false)
71 {
72 }
73
74 QueuedMeshUpdate::~QueuedMeshUpdate()
75 {
76         if(data)
77                 delete data;
78 }
79
80 /*
81         MeshUpdateQueue
82 */
83         
84 MeshUpdateQueue::MeshUpdateQueue()
85 {
86 }
87
88 MeshUpdateQueue::~MeshUpdateQueue()
89 {
90         JMutexAutoLock lock(m_mutex);
91
92         for(std::vector<QueuedMeshUpdate*>::iterator
93                         i = m_queue.begin();
94                         i != m_queue.end(); i++)
95         {
96                 QueuedMeshUpdate *q = *i;
97                 delete q;
98         }
99 }
100
101 /*
102         peer_id=0 adds with nobody to send to
103 */
104 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 {
106         DSTACK(__FUNCTION_NAME);
107
108         assert(data);
109
110         JMutexAutoLock lock(m_mutex);
111
112         if(urgent)
113                 m_urgents.insert(p);
114
115         /*
116                 Find if block is already in queue.
117                 If it is, update the data and quit.
118         */
119         for(std::vector<QueuedMeshUpdate*>::iterator
120                         i = m_queue.begin();
121                         i != m_queue.end(); i++)
122         {
123                 QueuedMeshUpdate *q = *i;
124                 if(q->p == p)
125                 {
126                         if(q->data)
127                                 delete q->data;
128                         q->data = data;
129                         if(ack_block_to_server)
130                                 q->ack_block_to_server = true;
131                         return;
132                 }
133         }
134         
135         /*
136                 Add the block
137         */
138         QueuedMeshUpdate *q = new QueuedMeshUpdate;
139         q->p = p;
140         q->data = data;
141         q->ack_block_to_server = ack_block_to_server;
142         m_queue.push_back(q);
143 }
144
145 // Returned pointer must be deleted
146 // Returns NULL if queue is empty
147 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 {
149         JMutexAutoLock lock(m_mutex);
150
151         bool must_be_urgent = !m_urgents.empty();
152         for(std::vector<QueuedMeshUpdate*>::iterator
153                         i = m_queue.begin();
154                         i != m_queue.end(); i++)
155         {
156                 QueuedMeshUpdate *q = *i;
157                 if(must_be_urgent && m_urgents.count(q->p) == 0)
158                         continue;
159                 m_queue.erase(i);
160                 m_urgents.erase(q->p);
161                 return q;
162         }
163         return NULL;
164 }
165
166 /*
167         MeshUpdateThread
168 */
169
170 void * MeshUpdateThread::Thread()
171 {
172         ThreadStarted();
173
174         log_register_thread("MeshUpdateThread");
175
176         DSTACK(__FUNCTION_NAME);
177         
178         BEGIN_DEBUG_EXCEPTION_HANDLER
179
180         while(getRun())
181         {
182                 /*// Wait for output queue to flush.
183                 // Allow 2 in queue, this makes less frametime jitter.
184                 // Umm actually, there is no much difference
185                 if(m_queue_out.size() >= 2)
186                 {
187                         sleep_ms(3);
188                         continue;
189                 }*/
190
191                 QueuedMeshUpdate *q = m_queue_in.pop();
192                 if(q == NULL)
193                 {
194                         sleep_ms(3);
195                         continue;
196                 }
197
198                 ScopeProfiler sp(g_profiler, "Client: Mesh making");
199
200                 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
201                 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
202                 {
203                         delete mesh_new;
204                         mesh_new = NULL;
205                 }
206
207                 MeshUpdateResult r;
208                 r.p = q->p;
209                 r.mesh = mesh_new;
210                 r.ack_block_to_server = q->ack_block_to_server;
211
212                 /*infostream<<"MeshUpdateThread: Processed "
213                                 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214                                 <<std::endl;*/
215
216                 m_queue_out.push_back(r);
217
218                 delete q;
219         }
220
221         END_DEBUG_EXCEPTION_HANDLER(errorstream)
222
223         return NULL;
224 }
225
226 /*
227         Client
228 */
229
230 Client::Client(
231                 IrrlichtDevice *device,
232                 const char *playername,
233                 std::string password,
234                 MapDrawControl &control,
235                 IWritableTextureSource *tsrc,
236                 IWritableShaderSource *shsrc,
237                 IWritableItemDefManager *itemdef,
238                 IWritableNodeDefManager *nodedef,
239                 ISoundManager *sound,
240                 MtEventManager *event,
241                 bool ipv6
242 ):
243         m_tsrc(tsrc),
244         m_shsrc(shsrc),
245         m_itemdef(itemdef),
246         m_nodedef(nodedef),
247         m_sound(sound),
248         m_event(event),
249         m_mesh_update_thread(this),
250         m_env(
251                 new ClientMap(this, this, control,
252                         device->getSceneManager()->getRootSceneNode(),
253                         device->getSceneManager(), 666),
254                 device->getSceneManager(),
255                 tsrc, this, device
256         ),
257         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
258         m_device(device),
259         m_server_ser_ver(SER_FMT_VER_INVALID),
260         m_playeritem(0),
261         m_inventory_updated(false),
262         m_inventory_from_server(NULL),
263         m_inventory_from_server_age(0.0),
264         m_animation_time(0),
265         m_crack_level(-1),
266         m_crack_pos(0,0,0),
267         m_map_seed(0),
268         m_password(password),
269         m_access_denied(false),
270         m_itemdef_received(false),
271         m_nodedef_received(false),
272         m_media_downloader(new ClientMediaDownloader()),
273         m_time_of_day_set(false),
274         m_last_time_of_day_f(-1),
275         m_time_of_day_update_timer(0),
276         m_recommended_send_interval(0.1),
277         m_removed_sounds_check_timer(0)
278 {
279         m_packetcounter_timer = 0.0;
280         //m_delete_unused_sectors_timer = 0.0;
281         m_connection_reinit_timer = 0.0;
282         m_avg_rtt_timer = 0.0;
283         m_playerpos_send_timer = 0.0;
284         m_ignore_damage_timer = 0.0;
285
286         /*
287                 Add local player
288         */
289         {
290                 Player *player = new LocalPlayer(this);
291
292                 player->updateName(playername);
293
294                 m_env.addPlayer(player);
295         }
296 }
297
298 Client::~Client()
299 {
300         {
301                 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
302                 m_con.Disconnect();
303         }
304
305         m_mesh_update_thread.setRun(false);
306         while(m_mesh_update_thread.IsRunning())
307                 sleep_ms(100);
308         while(!m_mesh_update_thread.m_queue_out.empty()) {
309                 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
310                 delete r.mesh;
311         }
312
313
314         delete m_inventory_from_server;
315
316         // Delete detached inventories
317         {
318                 for(std::map<std::string, Inventory*>::iterator
319                                 i = m_detached_inventories.begin();
320                                 i != m_detached_inventories.end(); i++){
321                         delete i->second;
322                 }
323         }
324
325         // cleanup 3d model meshes on client shutdown
326         while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
327                 scene::IAnimatedMesh * mesh =
328                         m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
329
330                 if (mesh != NULL)
331                         m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
332         }
333 }
334
335 void Client::connect(Address address)
336 {
337         DSTACK(__FUNCTION_NAME);
338         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
339         m_con.SetTimeoutMs(0);
340         m_con.Connect(address);
341 }
342
343 bool Client::connectedAndInitialized()
344 {
345         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
346
347         if(m_con.Connected() == false)
348                 return false;
349         
350         if(m_server_ser_ver == SER_FMT_VER_INVALID)
351                 return false;
352         
353         return true;
354 }
355
356 void Client::step(float dtime)
357 {
358         DSTACK(__FUNCTION_NAME);
359         
360         // Limit a bit
361         if(dtime > 2.0)
362                 dtime = 2.0;
363         
364         if(m_ignore_damage_timer > dtime)
365                 m_ignore_damage_timer -= dtime;
366         else
367                 m_ignore_damage_timer = 0.0;
368         
369         m_animation_time += dtime;
370         if(m_animation_time > 60.0)
371                 m_animation_time -= 60.0;
372
373         m_time_of_day_update_timer += dtime;
374         
375         //infostream<<"Client steps "<<dtime<<std::endl;
376
377         {
378                 //TimeTaker timer("ReceiveAll()", m_device);
379                 // 0ms
380                 ReceiveAll();
381         }
382         
383         {
384                 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
385                 // 0ms
386                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
387                 m_con.RunTimeouts(dtime);
388         }
389
390         /*
391                 Packet counter
392         */
393         {
394                 float &counter = m_packetcounter_timer;
395                 counter -= dtime;
396                 if(counter <= 0.0)
397                 {
398                         counter = 20.0;
399                         
400                         infostream<<"Client packetcounter (20s):"<<std::endl;
401                         m_packetcounter.print(infostream);
402                         m_packetcounter.clear();
403                 }
404         }
405         
406         // Get connection status
407         bool connected = connectedAndInitialized();
408
409 #if 0
410         {
411                 /*
412                         Delete unused sectors
413
414                         NOTE: This jams the game for a while because deleting sectors
415                               clear caches
416                 */
417                 
418                 float &counter = m_delete_unused_sectors_timer;
419                 counter -= dtime;
420                 if(counter <= 0.0)
421                 {
422                         // 3 minute interval
423                         //counter = 180.0;
424                         counter = 60.0;
425
426                         //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
427
428                         core::list<v3s16> deleted_blocks;
429
430                         float delete_unused_sectors_timeout =
431                                 g_settings->getFloat("client_delete_unused_sectors_timeout");
432         
433                         // Delete sector blocks
434                         /*u32 num = m_env.getMap().unloadUnusedData
435                                         (delete_unused_sectors_timeout,
436                                         true, &deleted_blocks);*/
437                         
438                         // Delete whole sectors
439                         m_env.getMap().unloadUnusedData
440                                         (delete_unused_sectors_timeout,
441                                         &deleted_blocks);
442
443                         if(deleted_blocks.size() > 0)
444                         {
445                                 /*infostream<<"Client: Deleted blocks of "<<num
446                                                 <<" unused sectors"<<std::endl;*/
447                                 /*infostream<<"Client: Deleted "<<num
448                                                 <<" unused sectors"<<std::endl;*/
449                                 
450                                 /*
451                                         Send info to server
452                                 */
453
454                                 // Env is locked so con can be locked.
455                                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
456                                 
457                                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
458                                 core::list<v3s16> sendlist;
459                                 for(;;)
460                                 {
461                                         if(sendlist.size() == 255 || i == deleted_blocks.end())
462                                         {
463                                                 if(sendlist.size() == 0)
464                                                         break;
465                                                 /*
466                                                         [0] u16 command
467                                                         [2] u8 count
468                                                         [3] v3s16 pos_0
469                                                         [3+6] v3s16 pos_1
470                                                         ...
471                                                 */
472                                                 u32 replysize = 2+1+6*sendlist.size();
473                                                 SharedBuffer<u8> reply(replysize);
474                                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
475                                                 reply[2] = sendlist.size();
476                                                 u32 k = 0;
477                                                 for(core::list<v3s16>::Iterator
478                                                                 j = sendlist.begin();
479                                                                 j != sendlist.end(); j++)
480                                                 {
481                                                         writeV3S16(&reply[2+1+6*k], *j);
482                                                         k++;
483                                                 }
484                                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
485
486                                                 if(i == deleted_blocks.end())
487                                                         break;
488
489                                                 sendlist.clear();
490                                         }
491
492                                         sendlist.push_back(*i);
493                                         i++;
494                                 }
495                         }
496                 }
497         }
498 #endif
499
500         if(connected == false)
501         {
502                 float &counter = m_connection_reinit_timer;
503                 counter -= dtime;
504                 if(counter <= 0.0)
505                 {
506                         counter = 2.0;
507
508                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
509                         
510                         Player *myplayer = m_env.getLocalPlayer();
511                         assert(myplayer != NULL);
512         
513                         // Send TOSERVER_INIT
514                         // [0] u16 TOSERVER_INIT
515                         // [2] u8 SER_FMT_VER_HIGHEST_READ
516                         // [3] u8[20] player_name
517                         // [23] u8[28] password (new in some version)
518                         // [51] u16 minimum supported network protocol version (added sometime)
519                         // [53] u16 maximum supported network protocol version (added later than the previous one)
520                         SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
521                         writeU16(&data[0], TOSERVER_INIT);
522                         writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
523
524                         memset((char*)&data[3], 0, PLAYERNAME_SIZE);
525                         snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
526
527                         /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
528                                         <<std::endl;*/
529
530                         memset((char*)&data[23], 0, PASSWORD_SIZE);
531                         snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
532                         
533                         writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
534                         writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
535
536                         // Send as unreliable
537                         Send(0, data, false);
538                 }
539
540                 // Not connected, return
541                 return;
542         }
543
544         /*
545                 Do stuff if connected
546         */
547         
548         /*
549                 Run Map's timers and unload unused data
550         */
551         const float map_timer_and_unload_dtime = 5.25;
552         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
553         {
554                 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
555                 std::list<v3s16> deleted_blocks;
556                 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
557                                 g_settings->getFloat("client_unload_unused_data_timeout"),
558                                 &deleted_blocks);
559                                 
560                 /*if(deleted_blocks.size() > 0)
561                         infostream<<"Client: Unloaded "<<deleted_blocks.size()
562                                         <<" unused blocks"<<std::endl;*/
563                         
564                 /*
565                         Send info to server
566                         NOTE: This loop is intentionally iterated the way it is.
567                 */
568
569                 std::list<v3s16>::iterator i = deleted_blocks.begin();
570                 std::list<v3s16> sendlist;
571                 for(;;)
572                 {
573                         if(sendlist.size() == 255 || i == deleted_blocks.end())
574                         {
575                                 if(sendlist.size() == 0)
576                                         break;
577                                 /*
578                                         [0] u16 command
579                                         [2] u8 count
580                                         [3] v3s16 pos_0
581                                         [3+6] v3s16 pos_1
582                                         ...
583                                 */
584                                 u32 replysize = 2+1+6*sendlist.size();
585                                 SharedBuffer<u8> reply(replysize);
586                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
587                                 reply[2] = sendlist.size();
588                                 u32 k = 0;
589                                 for(std::list<v3s16>::iterator
590                                                 j = sendlist.begin();
591                                                 j != sendlist.end(); ++j)
592                                 {
593                                         writeV3S16(&reply[2+1+6*k], *j);
594                                         k++;
595                                 }
596                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
597
598                                 if(i == deleted_blocks.end())
599                                         break;
600
601                                 sendlist.clear();
602                         }
603
604                         sendlist.push_back(*i);
605                         ++i;
606                 }
607         }
608
609         /*
610                 Handle environment
611         */
612         {
613                 // 0ms
614                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
615
616                 // Control local player (0ms)
617                 LocalPlayer *player = m_env.getLocalPlayer();
618                 assert(player != NULL);
619                 player->applyControl(dtime);
620
621                 //TimeTaker envtimer("env step", m_device);
622                 // Step environment
623                 m_env.step(dtime);
624                 
625                 /*
626                         Get events
627                 */
628                 for(;;)
629                 {
630                         ClientEnvEvent event = m_env.getClientEvent();
631                         if(event.type == CEE_NONE)
632                         {
633                                 break;
634                         }
635                         else if(event.type == CEE_PLAYER_DAMAGE)
636                         {
637                                 if(m_ignore_damage_timer <= 0)
638                                 {
639                                         u8 damage = event.player_damage.amount;
640                                         
641                                         if(event.player_damage.send_to_server)
642                                                 sendDamage(damage);
643
644                                         // Add to ClientEvent queue
645                                         ClientEvent event;
646                                         event.type = CE_PLAYER_DAMAGE;
647                                         event.player_damage.amount = damage;
648                                         m_client_event_queue.push_back(event);
649                                 }
650                         }
651                         else if(event.type == CEE_PLAYER_BREATH)
652                         {
653                                         u16 breath = event.player_breath.amount;
654                                         sendBreath(breath);
655                         }
656                 }
657         }
658
659         /*
660                 Print some info
661         */
662         {
663                 float &counter = m_avg_rtt_timer;
664                 counter += dtime;
665                 if(counter >= 10)
666                 {
667                         counter = 0.0;
668                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
669                         // connectedAndInitialized() is true, peer exists.
670                         float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
671                         infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
672                 }
673         }
674
675         /*
676                 Send player position to server
677         */
678         {
679                 float &counter = m_playerpos_send_timer;
680                 counter += dtime;
681                 if(counter >= m_recommended_send_interval)
682                 {
683                         counter = 0.0;
684                         sendPlayerPos();
685                 }
686         }
687
688         /*
689                 Replace updated meshes
690         */
691         {
692                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
693
694                 //TimeTaker timer("** Processing mesh update result queue");
695                 // 0ms
696                 
697                 /*infostream<<"Mesh update result queue size is "
698                                 <<m_mesh_update_thread.m_queue_out.size()
699                                 <<std::endl;*/
700                 
701                 int num_processed_meshes = 0;
702                 while(!m_mesh_update_thread.m_queue_out.empty())
703                 {
704                         num_processed_meshes++;
705                         MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
706                         MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
707                         if(block)
708                         {
709                                 //JMutexAutoLock lock(block->mesh_mutex);
710
711                                 // Delete the old mesh
712                                 if(block->mesh != NULL)
713                                 {
714                                         // TODO: Remove hardware buffers of meshbuffers of block->mesh
715                                         delete block->mesh;
716                                         block->mesh = NULL;
717                                 }
718
719                                 // Replace with the new mesh
720                                 block->mesh = r.mesh;
721                         } else {
722                                 delete r.mesh;
723                         }
724                         if(r.ack_block_to_server)
725                         {
726                                 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
727                                                 <<","<<r.p.Z<<")"<<std::endl;*/
728                                 /*
729                                         Acknowledge block
730                                 */
731                                 /*
732                                         [0] u16 command
733                                         [2] u8 count
734                                         [3] v3s16 pos_0
735                                         [3+6] v3s16 pos_1
736                                         ...
737                                 */
738                                 u32 replysize = 2+1+6;
739                                 SharedBuffer<u8> reply(replysize);
740                                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
741                                 reply[2] = 1;
742                                 writeV3S16(&reply[3], r.p);
743                                 // Send as reliable
744                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
745                         }
746                 }
747                 if(num_processed_meshes > 0)
748                         g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
749         }
750
751         /*
752                 Load fetched media
753         */
754         if (m_media_downloader && m_media_downloader->isStarted()) {
755                 m_media_downloader->step(this);
756                 if (m_media_downloader->isDone()) {
757                         delete m_media_downloader;
758                         m_media_downloader = NULL;
759                 }
760         }
761
762         /*
763                 If the server didn't update the inventory in a while, revert
764                 the local inventory (so the player notices the lag problem
765                 and knows something is wrong).
766         */
767         if(m_inventory_from_server)
768         {
769                 float interval = 10.0;
770                 float count_before = floor(m_inventory_from_server_age / interval);
771
772                 m_inventory_from_server_age += dtime;
773
774                 float count_after = floor(m_inventory_from_server_age / interval);
775
776                 if(count_after != count_before)
777                 {
778                         // Do this every <interval> seconds after TOCLIENT_INVENTORY
779                         // Reset the locally changed inventory to the authoritative inventory
780                         Player *player = m_env.getLocalPlayer();
781                         player->inventory = *m_inventory_from_server;
782                         m_inventory_updated = true;
783                 }
784         }
785
786         /*
787                 Update positions of sounds attached to objects
788         */
789         {
790                 for(std::map<int, u16>::iterator
791                                 i = m_sounds_to_objects.begin();
792                                 i != m_sounds_to_objects.end(); i++)
793                 {
794                         int client_id = i->first;
795                         u16 object_id = i->second;
796                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
797                         if(!cao)
798                                 continue;
799                         v3f pos = cao->getPosition();
800                         m_sound->updateSoundPosition(client_id, pos);
801                 }
802         }
803         
804         /*
805                 Handle removed remotely initiated sounds
806         */
807         m_removed_sounds_check_timer += dtime;
808         if(m_removed_sounds_check_timer >= 2.32)
809         {
810                 m_removed_sounds_check_timer = 0;
811                 // Find removed sounds and clear references to them
812                 std::set<s32> removed_server_ids;
813                 for(std::map<s32, int>::iterator
814                                 i = m_sounds_server_to_client.begin();
815                                 i != m_sounds_server_to_client.end();)
816                 {
817                         s32 server_id = i->first;
818                         int client_id = i->second;
819                         i++;
820                         if(!m_sound->soundExists(client_id)){
821                                 m_sounds_server_to_client.erase(server_id);
822                                 m_sounds_client_to_server.erase(client_id);
823                                 m_sounds_to_objects.erase(client_id);
824                                 removed_server_ids.insert(server_id);
825                         }
826                 }
827                 // Sync to server
828                 if(removed_server_ids.size() != 0)
829                 {
830                         std::ostringstream os(std::ios_base::binary);
831                         writeU16(os, TOSERVER_REMOVED_SOUNDS);
832                         writeU16(os, removed_server_ids.size());
833                         for(std::set<s32>::iterator i = removed_server_ids.begin();
834                                         i != removed_server_ids.end(); i++)
835                                 writeS32(os, *i);
836                         std::string s = os.str();
837                         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
838                         // Send as reliable
839                         Send(0, data, true);
840                 }
841         }
842 }
843
844 bool Client::loadMedia(const std::string &data, const std::string &filename)
845 {
846         // Silly irrlicht's const-incorrectness
847         Buffer<char> data_rw(data.c_str(), data.size());
848         
849         std::string name;
850
851         const char *image_ext[] = {
852                 ".png", ".jpg", ".bmp", ".tga",
853                 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
854                 NULL
855         };
856         name = removeStringEnd(filename, image_ext);
857         if(name != "")
858         {
859                 verbosestream<<"Client: Attempting to load image "
860                 <<"file \""<<filename<<"\""<<std::endl;
861
862                 io::IFileSystem *irrfs = m_device->getFileSystem();
863                 video::IVideoDriver *vdrv = m_device->getVideoDriver();
864
865                 // Create an irrlicht memory file
866                 io::IReadFile *rfile = irrfs->createMemoryReadFile(
867                                 *data_rw, data_rw.getSize(), "_tempreadfile");
868                 assert(rfile);
869                 // Read image
870                 video::IImage *img = vdrv->createImageFromFile(rfile);
871                 if(!img){
872                         errorstream<<"Client: Cannot create image from data of "
873                                         <<"file \""<<filename<<"\""<<std::endl;
874                         rfile->drop();
875                         return false;
876                 }
877                 else {
878                         m_tsrc->insertSourceImage(filename, img);
879                         img->drop();
880                         rfile->drop();
881                         return true;
882                 }
883         }
884
885         const char *sound_ext[] = {
886                 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
887                 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
888                 ".ogg", NULL
889         };
890         name = removeStringEnd(filename, sound_ext);
891         if(name != "")
892         {
893                 verbosestream<<"Client: Attempting to load sound "
894                 <<"file \""<<filename<<"\""<<std::endl;
895                 m_sound->loadSoundData(name, data);
896                 return true;
897         }
898
899         const char *model_ext[] = {
900                 ".x", ".b3d", ".md2", ".obj",
901                 NULL
902         };
903         name = removeStringEnd(filename, model_ext);
904         if(name != "")
905         {
906                 verbosestream<<"Client: Storing model into Irrlicht: "
907                                 <<"\""<<filename<<"\""<<std::endl;
908                 scene::ISceneManager *smgr = m_device->getSceneManager();
909
910                 //check if mesh was already cached
911                 scene::IAnimatedMesh *mesh =
912                         smgr->getMeshCache()->getMeshByName(filename.c_str());
913
914                 if (mesh != NULL) {
915                         errorstream << "Multiple models with name: " << filename.c_str() <<
916                                         " found replacing previous model!" << std::endl;
917
918                         smgr->getMeshCache()->removeMesh(mesh);
919                         mesh = 0;
920                 }
921
922                 io::IFileSystem *irrfs = m_device->getFileSystem();
923                 io::IReadFile *rfile = irrfs->createMemoryReadFile(
924                                 *data_rw, data_rw.getSize(), filename.c_str());
925                 assert(rfile);
926                 
927                 mesh = smgr->getMesh(rfile);
928                 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
929                 rfile->drop();
930                 return true;
931         }
932
933         errorstream<<"Client: Don't know how to load file \""
934                         <<filename<<"\""<<std::endl;
935         return false;
936 }
937
938 // Virtual methods from con::PeerHandler
939 void Client::peerAdded(con::Peer *peer)
940 {
941         infostream<<"Client::peerAdded(): peer->id="
942                         <<peer->id<<std::endl;
943 }
944 void Client::deletingPeer(con::Peer *peer, bool timeout)
945 {
946         infostream<<"Client::deletingPeer(): "
947                         "Server Peer is getting deleted "
948                         <<"(timeout="<<timeout<<")"<<std::endl;
949 }
950
951 /*
952         u16 command
953         u16 number of files requested
954         for each file {
955                 u16 length of name
956                 string name
957         }
958 */
959 void Client::request_media(const std::list<std::string> &file_requests)
960 {
961         std::ostringstream os(std::ios_base::binary);
962         writeU16(os, TOSERVER_REQUEST_MEDIA);
963         writeU16(os, file_requests.size());
964
965         for(std::list<std::string>::const_iterator i = file_requests.begin();
966                         i != file_requests.end(); ++i) {
967                 os<<serializeString(*i);
968         }
969
970         // Make data buffer
971         std::string s = os.str();
972         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
973         // Send as reliable
974         Send(0, data, true);
975         infostream<<"Client: Sending media request list to server ("
976                         <<file_requests.size()<<" files)"<<std::endl;
977 }
978
979 void Client::received_media()
980 {
981         // notify server we received everything
982         std::ostringstream os(std::ios_base::binary);
983         writeU16(os, TOSERVER_RECEIVED_MEDIA);
984         std::string s = os.str();
985         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
986         // Send as reliable
987         Send(0, data, true);
988         infostream<<"Client: Notifying server that we received all media"
989                         <<std::endl;
990 }
991
992 void Client::ReceiveAll()
993 {
994         DSTACK(__FUNCTION_NAME);
995         u32 start_ms = porting::getTimeMs();
996         for(;;)
997         {
998                 // Limit time even if there would be huge amounts of data to
999                 // process
1000                 if(porting::getTimeMs() > start_ms + 100)
1001                         break;
1002                 
1003                 try{
1004                         Receive();
1005                         g_profiler->graphAdd("client_received_packets", 1);
1006                 }
1007                 catch(con::NoIncomingDataException &e)
1008                 {
1009                         break;
1010                 }
1011                 catch(con::InvalidIncomingDataException &e)
1012                 {
1013                         infostream<<"Client::ReceiveAll(): "
1014                                         "InvalidIncomingDataException: what()="
1015                                         <<e.what()<<std::endl;
1016                 }
1017         }
1018 }
1019
1020 void Client::Receive()
1021 {
1022         DSTACK(__FUNCTION_NAME);
1023         SharedBuffer<u8> data;
1024         u16 sender_peer_id;
1025         u32 datasize;
1026         {
1027                 //TimeTaker t1("con mutex and receive", m_device);
1028                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1029                 datasize = m_con.Receive(sender_peer_id, data);
1030         }
1031         //TimeTaker t1("ProcessData", m_device);
1032         ProcessData(*data, datasize, sender_peer_id);
1033 }
1034
1035 /*
1036         sender_peer_id given to this shall be quaranteed to be a valid peer
1037 */
1038 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1039 {
1040         DSTACK(__FUNCTION_NAME);
1041
1042         // Ignore packets that don't even fit a command
1043         if(datasize < 2)
1044         {
1045                 m_packetcounter.add(60000);
1046                 return;
1047         }
1048
1049         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1050
1051         //infostream<<"Client: received command="<<command<<std::endl;
1052         m_packetcounter.add((u16)command);
1053         
1054         /*
1055                 If this check is removed, be sure to change the queue
1056                 system to know the ids
1057         */
1058         if(sender_peer_id != PEER_ID_SERVER)
1059         {
1060                 infostream<<"Client::ProcessData(): Discarding data not "
1061                                 "coming from server: peer_id="<<sender_peer_id
1062                                 <<std::endl;
1063                 return;
1064         }
1065
1066         u8 ser_version = m_server_ser_ver;
1067
1068         //infostream<<"Client received command="<<(int)command<<std::endl;
1069
1070         if(command == TOCLIENT_INIT)
1071         {
1072                 if(datasize < 3)
1073                         return;
1074
1075                 u8 deployed = data[2];
1076
1077                 infostream<<"Client: TOCLIENT_INIT received with "
1078                                 "deployed="<<((int)deployed&0xff)<<std::endl;
1079
1080                 if(!ser_ver_supported(deployed))
1081                 {
1082                         infostream<<"Client: TOCLIENT_INIT: Server sent "
1083                                         <<"unsupported ser_fmt_ver"<<std::endl;
1084                         return;
1085                 }
1086                 
1087                 m_server_ser_ver = deployed;
1088
1089                 // Get player position
1090                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1091                 if(datasize >= 2+1+6)
1092                         playerpos_s16 = readV3S16(&data[2+1]);
1093                 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1094
1095                 { //envlock
1096                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1097                         
1098                         // Set player position
1099                         Player *player = m_env.getLocalPlayer();
1100                         assert(player != NULL);
1101                         player->setPosition(playerpos_f);
1102                 }
1103                 
1104                 if(datasize >= 2+1+6+8)
1105                 {
1106                         // Get map seed
1107                         m_map_seed = readU64(&data[2+1+6]);
1108                         infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1109                 }
1110
1111                 if(datasize >= 2+1+6+8+4)
1112                 {
1113                         // Get map seed
1114                         m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1115                         infostream<<"Client: received recommended send interval "
1116                                         <<m_recommended_send_interval<<std::endl;
1117                 }
1118                 
1119                 // Reply to server
1120                 u32 replysize = 2;
1121                 SharedBuffer<u8> reply(replysize);
1122                 writeU16(&reply[0], TOSERVER_INIT2);
1123                 // Send as reliable
1124                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1125
1126                 return;
1127         }
1128
1129         if(command == TOCLIENT_ACCESS_DENIED)
1130         {
1131                 // The server didn't like our password. Note, this needs
1132                 // to be processed even if the serialisation format has
1133                 // not been agreed yet, the same as TOCLIENT_INIT.
1134                 m_access_denied = true;
1135                 m_access_denied_reason = L"Unknown";
1136                 if(datasize >= 4)
1137                 {
1138                         std::string datastring((char*)&data[2], datasize-2);
1139                         std::istringstream is(datastring, std::ios_base::binary);
1140                         m_access_denied_reason = deSerializeWideString(is);
1141                 }
1142                 return;
1143         }
1144
1145         if(ser_version == SER_FMT_VER_INVALID)
1146         {
1147                 infostream<<"Client: Server serialization"
1148                                 " format invalid or not initialized."
1149                                 " Skipping incoming command="<<command<<std::endl;
1150                 return;
1151         }
1152         
1153         // Just here to avoid putting the two if's together when
1154         // making some copypasta
1155         {}
1156
1157         if(command == TOCLIENT_REMOVENODE)
1158         {
1159                 if(datasize < 8)
1160                         return;
1161                 v3s16 p;
1162                 p.X = readS16(&data[2]);
1163                 p.Y = readS16(&data[4]);
1164                 p.Z = readS16(&data[6]);
1165                 
1166                 //TimeTaker t1("TOCLIENT_REMOVENODE");
1167                 
1168                 removeNode(p);
1169         }
1170         else if(command == TOCLIENT_ADDNODE)
1171         {
1172                 if(datasize < 8 + MapNode::serializedLength(ser_version))
1173                         return;
1174
1175                 v3s16 p;
1176                 p.X = readS16(&data[2]);
1177                 p.Y = readS16(&data[4]);
1178                 p.Z = readS16(&data[6]);
1179                 
1180                 //TimeTaker t1("TOCLIENT_ADDNODE");
1181
1182                 MapNode n;
1183                 n.deSerialize(&data[8], ser_version);
1184                 
1185                 bool remove_metadata = true;
1186                 u32 index = 8 + MapNode::serializedLength(ser_version);
1187                 if ((datasize >= index+1) && data[index]){
1188                         remove_metadata = false;
1189                 }
1190                 
1191                 addNode(p, n, remove_metadata);
1192         }
1193         else if(command == TOCLIENT_BLOCKDATA)
1194         {
1195                 // Ignore too small packet
1196                 if(datasize < 8)
1197                         return;
1198                         
1199                 v3s16 p;
1200                 p.X = readS16(&data[2]);
1201                 p.Y = readS16(&data[4]);
1202                 p.Z = readS16(&data[6]);
1203                 
1204                 /*infostream<<"Client: Thread: BLOCKDATA for ("
1205                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1206                 /*infostream<<"Client: Thread: BLOCKDATA for ("
1207                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1208                 
1209                 std::string datastring((char*)&data[8], datasize-8);
1210                 std::istringstream istr(datastring, std::ios_base::binary);
1211                 
1212                 MapSector *sector;
1213                 MapBlock *block;
1214                 
1215                 v2s16 p2d(p.X, p.Z);
1216                 sector = m_env.getMap().emergeSector(p2d);
1217                 
1218                 assert(sector->getPos() == p2d);
1219
1220                 //TimeTaker timer("MapBlock deSerialize");
1221                 // 0ms
1222                 
1223                 block = sector->getBlockNoCreateNoEx(p.Y);
1224                 if(block)
1225                 {
1226                         /*
1227                                 Update an existing block
1228                         */
1229                         //infostream<<"Updating"<<std::endl;
1230                         block->deSerialize(istr, ser_version, false);
1231                         block->deSerializeNetworkSpecific(istr);
1232                 }
1233                 else
1234                 {
1235                         /*
1236                                 Create a new block
1237                         */
1238                         //infostream<<"Creating new"<<std::endl;
1239                         block = new MapBlock(&m_env.getMap(), p, this);
1240                         block->deSerialize(istr, ser_version, false);
1241                         block->deSerializeNetworkSpecific(istr);
1242                         sector->insertBlock(block);
1243                 }
1244
1245 #if 0
1246                 /*
1247                         Acknowledge block
1248                 */
1249                 /*
1250                         [0] u16 command
1251                         [2] u8 count
1252                         [3] v3s16 pos_0
1253                         [3+6] v3s16 pos_1
1254                         ...
1255                 */
1256                 u32 replysize = 2+1+6;
1257                 SharedBuffer<u8> reply(replysize);
1258                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1259                 reply[2] = 1;
1260                 writeV3S16(&reply[3], p);
1261                 // Send as reliable
1262                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1263 #endif
1264
1265                 /*
1266                         Add it to mesh update queue and set it to be acknowledged after update.
1267                 */
1268                 //infostream<<"Adding mesh update task for received block"<<std::endl;
1269                 addUpdateMeshTaskWithEdge(p, true);
1270         }
1271         else if(command == TOCLIENT_INVENTORY)
1272         {
1273                 if(datasize < 3)
1274                         return;
1275
1276                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1277
1278                 { //envlock
1279                         //TimeTaker t2("mutex locking", m_device);
1280                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1281                         //t2.stop();
1282                         
1283                         //TimeTaker t3("istringstream init", m_device);
1284                         std::string datastring((char*)&data[2], datasize-2);
1285                         std::istringstream is(datastring, std::ios_base::binary);
1286                         //t3.stop();
1287                         
1288                         //TimeTaker t4("player get", m_device);
1289                         Player *player = m_env.getLocalPlayer();
1290                         assert(player != NULL);
1291                         //t4.stop();
1292
1293                         //TimeTaker t1("inventory.deSerialize()", m_device);
1294                         player->inventory.deSerialize(is);
1295                         //t1.stop();
1296
1297                         m_inventory_updated = true;
1298
1299                         delete m_inventory_from_server;
1300                         m_inventory_from_server = new Inventory(player->inventory);
1301                         m_inventory_from_server_age = 0.0;
1302
1303                         //infostream<<"Client got player inventory:"<<std::endl;
1304                         //player->inventory.print(infostream);
1305                 }
1306         }
1307         else if(command == TOCLIENT_TIME_OF_DAY)
1308         {
1309                 if(datasize < 4)
1310                         return;
1311                 
1312                 u16 time_of_day = readU16(&data[2]);
1313                 time_of_day = time_of_day % 24000;
1314                 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1315                 float time_speed = 0;
1316                 if(datasize >= 2 + 2 + 4){
1317                         time_speed = readF1000(&data[4]);
1318                 } else {
1319                         // Old message; try to approximate speed of time by ourselves
1320                         float time_of_day_f = (float)time_of_day / 24000.0;
1321                         float tod_diff_f = 0;
1322                         if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1323                                 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1324                         else
1325                                 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1326                         m_last_time_of_day_f = time_of_day_f;
1327                         float time_diff = m_time_of_day_update_timer;
1328                         m_time_of_day_update_timer = 0;
1329                         if(m_time_of_day_set){
1330                                 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1331                                 infostream<<"Client: Measured time_of_day speed (old format): "
1332                                                 <<time_speed<<" tod_diff_f="<<tod_diff_f
1333                                                 <<" time_diff="<<time_diff<<std::endl;
1334                         }
1335                 }
1336                 
1337                 // Update environment
1338                 m_env.setTimeOfDay(time_of_day);
1339                 m_env.setTimeOfDaySpeed(time_speed);
1340                 m_time_of_day_set = true;
1341
1342                 u32 dr = m_env.getDayNightRatio();
1343                 verbosestream<<"Client: time_of_day="<<time_of_day
1344                                 <<" time_speed="<<time_speed
1345                                 <<" dr="<<dr<<std::endl;
1346         }
1347         else if(command == TOCLIENT_CHAT_MESSAGE)
1348         {
1349                 /*
1350                         u16 command
1351                         u16 length
1352                         wstring message
1353                 */
1354                 u8 buf[6];
1355                 std::string datastring((char*)&data[2], datasize-2);
1356                 std::istringstream is(datastring, std::ios_base::binary);
1357                 
1358                 // Read stuff
1359                 is.read((char*)buf, 2);
1360                 u16 len = readU16(buf);
1361                 
1362                 std::wstring message;
1363                 for(u16 i=0; i<len; i++)
1364                 {
1365                         is.read((char*)buf, 2);
1366                         message += (wchar_t)readU16(buf);
1367                 }
1368
1369                 /*infostream<<"Client received chat message: "
1370                                 <<wide_to_narrow(message)<<std::endl;*/
1371                 
1372                 m_chat_queue.push_back(message);
1373         }
1374         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1375         {
1376                 //if(g_settings->getBool("enable_experimental"))
1377                 {
1378                         /*
1379                                 u16 command
1380                                 u16 count of removed objects
1381                                 for all removed objects {
1382                                         u16 id
1383                                 }
1384                                 u16 count of added objects
1385                                 for all added objects {
1386                                         u16 id
1387                                         u8 type
1388                                         u32 initialization data length
1389                                         string initialization data
1390                                 }
1391                         */
1392
1393                         char buf[6];
1394                         // Get all data except the command number
1395                         std::string datastring((char*)&data[2], datasize-2);
1396                         // Throw them in an istringstream
1397                         std::istringstream is(datastring, std::ios_base::binary);
1398
1399                         // Read stuff
1400                         
1401                         // Read removed objects
1402                         is.read(buf, 2);
1403                         u16 removed_count = readU16((u8*)buf);
1404                         for(u16 i=0; i<removed_count; i++)
1405                         {
1406                                 is.read(buf, 2);
1407                                 u16 id = readU16((u8*)buf);
1408                                 // Remove it
1409                                 {
1410                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1411                                         m_env.removeActiveObject(id);
1412                                 }
1413                         }
1414                         
1415                         // Read added objects
1416                         is.read(buf, 2);
1417                         u16 added_count = readU16((u8*)buf);
1418                         for(u16 i=0; i<added_count; i++)
1419                         {
1420                                 is.read(buf, 2);
1421                                 u16 id = readU16((u8*)buf);
1422                                 is.read(buf, 1);
1423                                 u8 type = readU8((u8*)buf);
1424                                 std::string data = deSerializeLongString(is);
1425                                 // Add it
1426                                 {
1427                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1428                                         m_env.addActiveObject(id, type, data);
1429                                 }
1430                         }
1431                 }
1432         }
1433         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1434         {
1435                 //if(g_settings->getBool("enable_experimental"))
1436                 {
1437                         /*
1438                                 u16 command
1439                                 for all objects
1440                                 {
1441                                         u16 id
1442                                         u16 message length
1443                                         string message
1444                                 }
1445                         */
1446                         char buf[6];
1447                         // Get all data except the command number
1448                         std::string datastring((char*)&data[2], datasize-2);
1449                         // Throw them in an istringstream
1450                         std::istringstream is(datastring, std::ios_base::binary);
1451                         
1452                         while(is.eof() == false)
1453                         {
1454                                 // Read stuff
1455                                 is.read(buf, 2);
1456                                 u16 id = readU16((u8*)buf);
1457                                 if(is.eof())
1458                                         break;
1459                                 is.read(buf, 2);
1460                                 u16 message_size = readU16((u8*)buf);
1461                                 std::string message;
1462                                 message.reserve(message_size);
1463                                 for(u16 i=0; i<message_size; i++)
1464                                 {
1465                                         is.read(buf, 1);
1466                                         message.append(buf, 1);
1467                                 }
1468                                 // Pass on to the environment
1469                                 {
1470                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1471                                         m_env.processActiveObjectMessage(id, message);
1472                                 }
1473                         }
1474                 }
1475         }
1476         else if(command == TOCLIENT_MOVEMENT)
1477         {
1478                 std::string datastring((char*)&data[2], datasize-2);
1479                 std::istringstream is(datastring, std::ios_base::binary);
1480                 Player *player = m_env.getLocalPlayer();
1481                 assert(player != NULL);
1482
1483                 player->movement_acceleration_default = readF1000(is) * BS;
1484                 player->movement_acceleration_air = readF1000(is) * BS;
1485                 player->movement_acceleration_fast = readF1000(is) * BS;
1486                 player->movement_speed_walk = readF1000(is) * BS;
1487                 player->movement_speed_crouch = readF1000(is) * BS;
1488                 player->movement_speed_fast = readF1000(is) * BS;
1489                 player->movement_speed_climb = readF1000(is) * BS;
1490                 player->movement_speed_jump = readF1000(is) * BS;
1491                 player->movement_liquid_fluidity = readF1000(is) * BS;
1492                 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1493                 player->movement_liquid_sink = readF1000(is) * BS;
1494                 player->movement_gravity = readF1000(is) * BS;
1495         }
1496         else if(command == TOCLIENT_HP)
1497         {
1498                 std::string datastring((char*)&data[2], datasize-2);
1499                 std::istringstream is(datastring, std::ios_base::binary);
1500                 Player *player = m_env.getLocalPlayer();
1501                 assert(player != NULL);
1502                 u8 oldhp = player->hp;
1503                 u8 hp = readU8(is);
1504                 player->hp = hp;
1505
1506                 if(hp < oldhp)
1507                 {
1508                         // Add to ClientEvent queue
1509                         ClientEvent event;
1510                         event.type = CE_PLAYER_DAMAGE;
1511                         event.player_damage.amount = oldhp - hp;
1512                         m_client_event_queue.push_back(event);
1513                 }
1514         }
1515         else if(command == TOCLIENT_BREATH)
1516         {
1517                 std::string datastring((char*)&data[2], datasize-2);
1518                 std::istringstream is(datastring, std::ios_base::binary);
1519                 Player *player = m_env.getLocalPlayer();
1520                 assert(player != NULL);
1521                 u16 breath = readU16(is);
1522                 player->setBreath(breath) ;
1523         }
1524         else if(command == TOCLIENT_MOVE_PLAYER)
1525         {
1526                 std::string datastring((char*)&data[2], datasize-2);
1527                 std::istringstream is(datastring, std::ios_base::binary);
1528                 Player *player = m_env.getLocalPlayer();
1529                 assert(player != NULL);
1530                 v3f pos = readV3F1000(is);
1531                 f32 pitch = readF1000(is);
1532                 f32 yaw = readF1000(is);
1533                 player->setPosition(pos);
1534                 /*player->setPitch(pitch);
1535                 player->setYaw(yaw);*/
1536
1537                 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1538                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1539                                 <<" pitch="<<pitch
1540                                 <<" yaw="<<yaw
1541                                 <<std::endl;
1542
1543                 /*
1544                         Add to ClientEvent queue.
1545                         This has to be sent to the main program because otherwise
1546                         it would just force the pitch and yaw values to whatever
1547                         the camera points to.
1548                 */
1549                 ClientEvent event;
1550                 event.type = CE_PLAYER_FORCE_MOVE;
1551                 event.player_force_move.pitch = pitch;
1552                 event.player_force_move.yaw = yaw;
1553                 m_client_event_queue.push_back(event);
1554
1555                 // Ignore damage for a few seconds, so that the player doesn't
1556                 // get damage from falling on ground
1557                 m_ignore_damage_timer = 3.0;
1558         }
1559         else if(command == TOCLIENT_PLAYERITEM)
1560         {
1561                 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1562         }
1563         else if(command == TOCLIENT_DEATHSCREEN)
1564         {
1565                 std::string datastring((char*)&data[2], datasize-2);
1566                 std::istringstream is(datastring, std::ios_base::binary);
1567                 
1568                 bool set_camera_point_target = readU8(is);
1569                 v3f camera_point_target = readV3F1000(is);
1570                 
1571                 ClientEvent event;
1572                 event.type = CE_DEATHSCREEN;
1573                 event.deathscreen.set_camera_point_target = set_camera_point_target;
1574                 event.deathscreen.camera_point_target_x = camera_point_target.X;
1575                 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1576                 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1577                 m_client_event_queue.push_back(event);
1578         }
1579         else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1580         {
1581                 std::string datastring((char*)&data[2], datasize-2);
1582                 std::istringstream is(datastring, std::ios_base::binary);
1583
1584                 int num_files = readU16(is);
1585                 
1586                 infostream<<"Client: Received media announcement: packet size: "
1587                                 <<datasize<<std::endl;
1588
1589                 if (m_media_downloader == NULL ||
1590                                 m_media_downloader->isStarted()) {
1591                         const char *problem = m_media_downloader ?
1592                                 "we already saw another announcement" :
1593                                 "all media has been received already";
1594                         errorstream<<"Client: Received media announcement but "
1595                                 <<problem<<"! "
1596                                 <<" files="<<num_files
1597                                 <<" size="<<datasize<<std::endl;
1598                         return;
1599                 }
1600
1601                 // Mesh update thread must be stopped while
1602                 // updating content definitions
1603                 assert(!m_mesh_update_thread.IsRunning());
1604
1605                 for(int i=0; i<num_files; i++)
1606                 {
1607                         std::string name = deSerializeString(is);
1608                         std::string sha1_base64 = deSerializeString(is);
1609                         std::string sha1_raw = base64_decode(sha1_base64);
1610                         m_media_downloader->addFile(name, sha1_raw);
1611                 }
1612
1613                 std::vector<std::string> remote_media;
1614                 try {
1615                         Strfnd sf(deSerializeString(is));
1616                         while(!sf.atend()) {
1617                                 std::string baseurl = trim(sf.next(","));
1618                                 if(baseurl != "")
1619                                         m_media_downloader->addRemoteServer(baseurl);
1620                         }
1621                 }
1622                 catch(SerializationError) {
1623                         // not supported by server or turned off
1624                 }
1625
1626                 m_media_downloader->step(this);
1627                 if (m_media_downloader->isDone()) {
1628                         // might be done already if all media is in the cache
1629                         delete m_media_downloader;
1630                         m_media_downloader = NULL;
1631                 }
1632         }
1633         else if(command == TOCLIENT_MEDIA)
1634         {
1635                 std::string datastring((char*)&data[2], datasize-2);
1636                 std::istringstream is(datastring, std::ios_base::binary);
1637
1638                 /*
1639                         u16 command
1640                         u16 total number of file bunches
1641                         u16 index of this bunch
1642                         u32 number of files in this bunch
1643                         for each file {
1644                                 u16 length of name
1645                                 string name
1646                                 u32 length of data
1647                                 data
1648                         }
1649                 */
1650                 int num_bunches = readU16(is);
1651                 int bunch_i = readU16(is);
1652                 u32 num_files = readU32(is);
1653                 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1654                                 <<num_bunches<<" files="<<num_files
1655                                 <<" size="<<datasize<<std::endl;
1656
1657                 if (num_files == 0)
1658                         return;
1659
1660                 if (m_media_downloader == NULL ||
1661                                 !m_media_downloader->isStarted()) {
1662                         const char *problem = m_media_downloader ?
1663                                 "media has not been requested" :
1664                                 "all media has been received already";
1665                         errorstream<<"Client: Received media but "
1666                                 <<problem<<"! "
1667                                 <<" bunch "<<bunch_i<<"/"<<num_bunches
1668                                 <<" files="<<num_files
1669                                 <<" size="<<datasize<<std::endl;
1670                         return;
1671                 }
1672
1673                 // Mesh update thread must be stopped while
1674                 // updating content definitions
1675                 assert(!m_mesh_update_thread.IsRunning());
1676
1677                 for(u32 i=0; i<num_files; i++){
1678                         std::string name = deSerializeString(is);
1679                         std::string data = deSerializeLongString(is);
1680                         m_media_downloader->conventionalTransferDone(
1681                                         name, data, this);
1682                 }
1683
1684                 if (m_media_downloader->isDone()) {
1685                         delete m_media_downloader;
1686                         m_media_downloader = NULL;
1687                 }
1688         }
1689         else if(command == TOCLIENT_TOOLDEF)
1690         {
1691                 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1692         }
1693         else if(command == TOCLIENT_NODEDEF)
1694         {
1695                 infostream<<"Client: Received node definitions: packet size: "
1696                                 <<datasize<<std::endl;
1697
1698                 // Mesh update thread must be stopped while
1699                 // updating content definitions
1700                 assert(!m_mesh_update_thread.IsRunning());
1701
1702                 // Decompress node definitions
1703                 std::string datastring((char*)&data[2], datasize-2);
1704                 std::istringstream is(datastring, std::ios_base::binary);
1705                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1706                 std::ostringstream tmp_os;
1707                 decompressZlib(tmp_is, tmp_os);
1708
1709                 // Deserialize node definitions
1710                 std::istringstream tmp_is2(tmp_os.str());
1711                 m_nodedef->deSerialize(tmp_is2);
1712                 m_nodedef_received = true;
1713         }
1714         else if(command == TOCLIENT_CRAFTITEMDEF)
1715         {
1716                 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1717         }
1718         else if(command == TOCLIENT_ITEMDEF)
1719         {
1720                 infostream<<"Client: Received item definitions: packet size: "
1721                                 <<datasize<<std::endl;
1722
1723                 // Mesh update thread must be stopped while
1724                 // updating content definitions
1725                 assert(!m_mesh_update_thread.IsRunning());
1726
1727                 // Decompress item definitions
1728                 std::string datastring((char*)&data[2], datasize-2);
1729                 std::istringstream is(datastring, std::ios_base::binary);
1730                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1731                 std::ostringstream tmp_os;
1732                 decompressZlib(tmp_is, tmp_os);
1733
1734                 // Deserialize node definitions
1735                 std::istringstream tmp_is2(tmp_os.str());
1736                 m_itemdef->deSerialize(tmp_is2);
1737                 m_itemdef_received = true;
1738         }
1739         else if(command == TOCLIENT_PLAY_SOUND)
1740         {
1741                 std::string datastring((char*)&data[2], datasize-2);
1742                 std::istringstream is(datastring, std::ios_base::binary);
1743
1744                 s32 server_id = readS32(is);
1745                 std::string name = deSerializeString(is);
1746                 float gain = readF1000(is);
1747                 int type = readU8(is); // 0=local, 1=positional, 2=object
1748                 v3f pos = readV3F1000(is);
1749                 u16 object_id = readU16(is);
1750                 bool loop = readU8(is);
1751                 // Start playing
1752                 int client_id = -1;
1753                 switch(type){
1754                 case 0: // local
1755                         client_id = m_sound->playSound(name, loop, gain);
1756                         break;
1757                 case 1: // positional
1758                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
1759                         break;
1760                 case 2: { // object
1761                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
1762                         if(cao)
1763                                 pos = cao->getPosition();
1764                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
1765                         // TODO: Set up sound to move with object
1766                         break; }
1767                 default:
1768                         break;
1769                 }
1770                 if(client_id != -1){
1771                         m_sounds_server_to_client[server_id] = client_id;
1772                         m_sounds_client_to_server[client_id] = server_id;
1773                         if(object_id != 0)
1774                                 m_sounds_to_objects[client_id] = object_id;
1775                 }
1776         }
1777         else if(command == TOCLIENT_STOP_SOUND)
1778         {
1779                 std::string datastring((char*)&data[2], datasize-2);
1780                 std::istringstream is(datastring, std::ios_base::binary);
1781
1782                 s32 server_id = readS32(is);
1783                 std::map<s32, int>::iterator i =
1784                                 m_sounds_server_to_client.find(server_id);
1785                 if(i != m_sounds_server_to_client.end()){
1786                         int client_id = i->second;
1787                         m_sound->stopSound(client_id);
1788                 }
1789         }
1790         else if(command == TOCLIENT_PRIVILEGES)
1791         {
1792                 std::string datastring((char*)&data[2], datasize-2);
1793                 std::istringstream is(datastring, std::ios_base::binary);
1794                 
1795                 m_privileges.clear();
1796                 infostream<<"Client: Privileges updated: ";
1797                 u16 num_privileges = readU16(is);
1798                 for(u16 i=0; i<num_privileges; i++){
1799                         std::string priv = deSerializeString(is);
1800                         m_privileges.insert(priv);
1801                         infostream<<priv<<" ";
1802                 }
1803                 infostream<<std::endl;
1804         }
1805         else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1806         {
1807                 std::string datastring((char*)&data[2], datasize-2);
1808                 std::istringstream is(datastring, std::ios_base::binary);
1809
1810                 // Store formspec in LocalPlayer
1811                 Player *player = m_env.getLocalPlayer();
1812                 assert(player != NULL);
1813                 player->inventory_formspec = deSerializeLongString(is);
1814         }
1815         else if(command == TOCLIENT_DETACHED_INVENTORY)
1816         {
1817                 std::string datastring((char*)&data[2], datasize-2);
1818                 std::istringstream is(datastring, std::ios_base::binary);
1819
1820                 std::string name = deSerializeString(is);
1821                 
1822                 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1823
1824                 Inventory *inv = NULL;
1825                 if(m_detached_inventories.count(name) > 0)
1826                         inv = m_detached_inventories[name];
1827                 else{
1828                         inv = new Inventory(m_itemdef);
1829                         m_detached_inventories[name] = inv;
1830                 }
1831                 inv->deSerialize(is);
1832         }
1833         else if(command == TOCLIENT_SHOW_FORMSPEC)
1834         {
1835                 std::string datastring((char*)&data[2], datasize-2);
1836                 std::istringstream is(datastring, std::ios_base::binary);
1837
1838                 std::string formspec = deSerializeLongString(is);
1839                 std::string formname = deSerializeString(is);
1840
1841                 ClientEvent event;
1842                 event.type = CE_SHOW_FORMSPEC;
1843                 // pointer is required as event is a struct only!
1844                 // adding a std:string to a struct isn't possible
1845                 event.show_formspec.formspec = new std::string(formspec);
1846                 event.show_formspec.formname = new std::string(formname);
1847                 m_client_event_queue.push_back(event);
1848         }
1849         else if(command == TOCLIENT_SPAWN_PARTICLE)
1850         {
1851                 std::string datastring((char*)&data[2], datasize-2);
1852                 std::istringstream is(datastring, std::ios_base::binary);
1853
1854                 v3f pos = readV3F1000(is);
1855                 v3f vel = readV3F1000(is);
1856                 v3f acc = readV3F1000(is);
1857                 float expirationtime = readF1000(is);
1858                 float size = readF1000(is);
1859                 bool collisiondetection = readU8(is);
1860                 std::string texture = deSerializeLongString(is);
1861
1862                 ClientEvent event;
1863                 event.type = CE_SPAWN_PARTICLE;
1864                 event.spawn_particle.pos = new v3f (pos);
1865                 event.spawn_particle.vel = new v3f (vel);
1866                 event.spawn_particle.acc = new v3f (acc);
1867
1868                 event.spawn_particle.expirationtime = expirationtime;
1869                 event.spawn_particle.size = size;
1870                 event.spawn_particle.collisiondetection =
1871                                 collisiondetection;
1872                 event.spawn_particle.texture = new std::string(texture);
1873
1874                 m_client_event_queue.push_back(event);
1875         }
1876         else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1877         {
1878                 std::string datastring((char*)&data[2], datasize-2);
1879                 std::istringstream is(datastring, std::ios_base::binary);
1880
1881                 u16 amount = readU16(is);
1882                 float spawntime = readF1000(is);
1883                 v3f minpos = readV3F1000(is);
1884                 v3f maxpos = readV3F1000(is);
1885                 v3f minvel = readV3F1000(is);
1886                 v3f maxvel = readV3F1000(is);
1887                 v3f minacc = readV3F1000(is);
1888                 v3f maxacc = readV3F1000(is);
1889                 float minexptime = readF1000(is);
1890                 float maxexptime = readF1000(is);
1891                 float minsize = readF1000(is);
1892                 float maxsize = readF1000(is);
1893                 bool collisiondetection = readU8(is);
1894                 std::string texture = deSerializeLongString(is);
1895                 u32 id = readU32(is);
1896
1897                 ClientEvent event;
1898                 event.type = CE_ADD_PARTICLESPAWNER;
1899                 event.add_particlespawner.amount = amount;
1900                 event.add_particlespawner.spawntime = spawntime;
1901
1902                 event.add_particlespawner.minpos = new v3f (minpos);
1903                 event.add_particlespawner.maxpos = new v3f (maxpos);
1904                 event.add_particlespawner.minvel = new v3f (minvel);
1905                 event.add_particlespawner.maxvel = new v3f (maxvel);
1906                 event.add_particlespawner.minacc = new v3f (minacc);
1907                 event.add_particlespawner.maxacc = new v3f (maxacc);
1908
1909                 event.add_particlespawner.minexptime = minexptime;
1910                 event.add_particlespawner.maxexptime = maxexptime;
1911                 event.add_particlespawner.minsize = minsize;
1912                 event.add_particlespawner.maxsize = maxsize;
1913                 event.add_particlespawner.collisiondetection = collisiondetection;
1914                 event.add_particlespawner.texture = new std::string(texture);
1915                 event.add_particlespawner.id = id;
1916
1917                 m_client_event_queue.push_back(event);
1918         }
1919         else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1920         {
1921                 std::string datastring((char*)&data[2], datasize-2);
1922                 std::istringstream is(datastring, std::ios_base::binary);
1923
1924                 u32 id = readU16(is);
1925
1926                 ClientEvent event;
1927                 event.type = CE_DELETE_PARTICLESPAWNER;
1928                 event.delete_particlespawner.id = id;
1929
1930                 m_client_event_queue.push_back(event);
1931         }
1932         else if(command == TOCLIENT_HUDADD)
1933         {
1934                 std::string datastring((char *)&data[2], datasize - 2);
1935                 std::istringstream is(datastring, std::ios_base::binary);
1936
1937                 u32 id           = readU32(is);
1938                 u8 type          = readU8(is);
1939                 v2f pos          = readV2F1000(is);
1940                 std::string name = deSerializeString(is);
1941                 v2f scale        = readV2F1000(is);
1942                 std::string text = deSerializeString(is);
1943                 u32 number       = readU32(is);
1944                 u32 item         = readU32(is);
1945                 u32 dir          = readU32(is);
1946                 v2f align        = readV2F1000(is);
1947                 v2f offset       = readV2F1000(is);
1948
1949                 ClientEvent event;
1950                 event.type = CE_HUDADD;
1951                 event.hudadd.id     = id;
1952                 event.hudadd.type   = type;
1953                 event.hudadd.pos    = new v2f(pos);
1954                 event.hudadd.name   = new std::string(name);
1955                 event.hudadd.scale  = new v2f(scale);
1956                 event.hudadd.text   = new std::string(text);
1957                 event.hudadd.number = number;
1958                 event.hudadd.item   = item;
1959                 event.hudadd.dir    = dir;
1960                 event.hudadd.align  = new v2f(align);
1961                 event.hudadd.offset = new v2f(offset);
1962                 m_client_event_queue.push_back(event);
1963         }
1964         else if(command == TOCLIENT_HUDRM)
1965         {
1966                 std::string datastring((char *)&data[2], datasize - 2);
1967                 std::istringstream is(datastring, std::ios_base::binary);
1968
1969                 u32 id = readU32(is);
1970
1971                 ClientEvent event;
1972                 event.type = CE_HUDRM;
1973                 event.hudrm.id = id;
1974                 m_client_event_queue.push_back(event);
1975         }
1976         else if(command == TOCLIENT_HUDCHANGE)
1977         {
1978                 std::string sdata;
1979                 v2f v2fdata;
1980                 u32 intdata = 0;
1981                 
1982                 std::string datastring((char *)&data[2], datasize - 2);
1983                 std::istringstream is(datastring, std::ios_base::binary);
1984
1985                 u32 id  = readU32(is);
1986                 u8 stat = (HudElementStat)readU8(is);
1987                 
1988                 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1989                         stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1990                         v2fdata = readV2F1000(is);
1991                 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1992                         sdata = deSerializeString(is);
1993                 else
1994                         intdata = readU32(is);
1995                 
1996                 ClientEvent event;
1997                 event.type = CE_HUDCHANGE;
1998                 event.hudchange.id      = id;
1999                 event.hudchange.stat    = (HudElementStat)stat;
2000                 event.hudchange.v2fdata = new v2f(v2fdata);
2001                 event.hudchange.sdata   = new std::string(sdata);
2002                 event.hudchange.data    = intdata;
2003                 m_client_event_queue.push_back(event);
2004         }
2005         else if(command == TOCLIENT_HUD_SET_FLAGS)
2006         {
2007                 std::string datastring((char *)&data[2], datasize - 2);
2008                 std::istringstream is(datastring, std::ios_base::binary);
2009
2010                 Player *player = m_env.getLocalPlayer();
2011                 assert(player != NULL);
2012
2013                 u32 flags = readU32(is);
2014                 u32 mask  = readU32(is);
2015                 
2016                 player->hud_flags &= ~mask;
2017                 player->hud_flags |= flags;
2018         }
2019         else if(command == TOCLIENT_HUD_SET_PARAM)
2020         {
2021                 std::string datastring((char *)&data[2], datasize - 2);
2022                 std::istringstream is(datastring, std::ios_base::binary);
2023
2024                 Player *player = m_env.getLocalPlayer();
2025                 assert(player != NULL);
2026
2027                 u16 param         = readU16(is);
2028                 std::string value = deSerializeString(is);
2029
2030                 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2031                         s32 hotbar_itemcount = readS32((u8*) value.c_str());
2032                         if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2033                                 player->hud_hotbar_itemcount = hotbar_itemcount;
2034                 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2035                         ((LocalPlayer *) player)->hotbar_image = value;
2036                 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2037                         ((LocalPlayer *) player)->hotbar_selected_image = value;
2038                 }
2039         }
2040         else
2041         {
2042                 infostream<<"Client: Ignoring unknown command "
2043                                 <<command<<std::endl;
2044         }
2045 }
2046
2047 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2048 {
2049         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2050         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2051 }
2052
2053 void Client::interact(u8 action, const PointedThing& pointed)
2054 {
2055         if(connectedAndInitialized() == false){
2056                 infostream<<"Client::interact() "
2057                                 "cancelled (not connected)"
2058                                 <<std::endl;
2059                 return;
2060         }
2061
2062         std::ostringstream os(std::ios_base::binary);
2063
2064         /*
2065                 [0] u16 command
2066                 [2] u8 action
2067                 [3] u16 item
2068                 [5] u32 length of the next item
2069                 [9] serialized PointedThing
2070                 actions:
2071                 0: start digging (from undersurface) or use
2072                 1: stop digging (all parameters ignored)
2073                 2: digging completed
2074                 3: place block or item (to abovesurface)
2075                 4: use item
2076         */
2077         writeU16(os, TOSERVER_INTERACT);
2078         writeU8(os, action);
2079         writeU16(os, getPlayerItem());
2080         std::ostringstream tmp_os(std::ios::binary);
2081         pointed.serialize(tmp_os);
2082         os<<serializeLongString(tmp_os.str());
2083
2084         std::string s = os.str();
2085         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2086
2087         // Send as reliable
2088         Send(0, data, true);
2089 }
2090
2091 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2092                 const std::map<std::string, std::string> &fields)
2093 {
2094         std::ostringstream os(std::ios_base::binary);
2095
2096         writeU16(os, TOSERVER_NODEMETA_FIELDS);
2097         writeV3S16(os, p);
2098         os<<serializeString(formname);
2099         writeU16(os, fields.size());
2100         for(std::map<std::string, std::string>::const_iterator
2101                         i = fields.begin(); i != fields.end(); i++){
2102                 const std::string &name = i->first;
2103                 const std::string &value = i->second;
2104                 os<<serializeString(name);
2105                 os<<serializeLongString(value);
2106         }
2107
2108         // Make data buffer
2109         std::string s = os.str();
2110         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2111         // Send as reliable
2112         Send(0, data, true);
2113 }
2114         
2115 void Client::sendInventoryFields(const std::string &formname,
2116                 const std::map<std::string, std::string> &fields)
2117 {
2118         std::ostringstream os(std::ios_base::binary);
2119
2120         writeU16(os, TOSERVER_INVENTORY_FIELDS);
2121         os<<serializeString(formname);
2122         writeU16(os, fields.size());
2123         for(std::map<std::string, std::string>::const_iterator
2124                         i = fields.begin(); i != fields.end(); i++){
2125                 const std::string &name = i->first;
2126                 const std::string &value = i->second;
2127                 os<<serializeString(name);
2128                 os<<serializeLongString(value);
2129         }
2130
2131         // Make data buffer
2132         std::string s = os.str();
2133         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2134         // Send as reliable
2135         Send(0, data, true);
2136 }
2137
2138 void Client::sendInventoryAction(InventoryAction *a)
2139 {
2140         std::ostringstream os(std::ios_base::binary);
2141         u8 buf[12];
2142         
2143         // Write command
2144         writeU16(buf, TOSERVER_INVENTORY_ACTION);
2145         os.write((char*)buf, 2);
2146
2147         a->serialize(os);
2148         
2149         // Make data buffer
2150         std::string s = os.str();
2151         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2152         // Send as reliable
2153         Send(0, data, true);
2154 }
2155
2156 void Client::sendChatMessage(const std::wstring &message)
2157 {
2158         std::ostringstream os(std::ios_base::binary);
2159         u8 buf[12];
2160         
2161         // Write command
2162         writeU16(buf, TOSERVER_CHAT_MESSAGE);
2163         os.write((char*)buf, 2);
2164         
2165         // Write length
2166         writeU16(buf, message.size());
2167         os.write((char*)buf, 2);
2168         
2169         // Write string
2170         for(u32 i=0; i<message.size(); i++)
2171         {
2172                 u16 w = message[i];
2173                 writeU16(buf, w);
2174                 os.write((char*)buf, 2);
2175         }
2176         
2177         // Make data buffer
2178         std::string s = os.str();
2179         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2180         // Send as reliable
2181         Send(0, data, true);
2182 }
2183
2184 void Client::sendChangePassword(const std::wstring oldpassword,
2185                 const std::wstring newpassword)
2186 {
2187         Player *player = m_env.getLocalPlayer();
2188         if(player == NULL)
2189                 return;
2190
2191         std::string playername = player->getName();
2192         std::string oldpwd = translatePassword(playername, oldpassword);
2193         std::string newpwd = translatePassword(playername, newpassword);
2194
2195         std::ostringstream os(std::ios_base::binary);
2196         u8 buf[2+PASSWORD_SIZE*2];
2197         /*
2198                 [0] u16 TOSERVER_PASSWORD
2199                 [2] u8[28] old password
2200                 [30] u8[28] new password
2201         */
2202
2203         writeU16(buf, TOSERVER_PASSWORD);
2204         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2205         {
2206                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2207                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2208         }
2209         buf[2+PASSWORD_SIZE-1] = 0;
2210         buf[30+PASSWORD_SIZE-1] = 0;
2211         os.write((char*)buf, 2+PASSWORD_SIZE*2);
2212
2213         // Make data buffer
2214         std::string s = os.str();
2215         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2216         // Send as reliable
2217         Send(0, data, true);
2218 }
2219
2220
2221 void Client::sendDamage(u8 damage)
2222 {
2223         DSTACK(__FUNCTION_NAME);
2224         std::ostringstream os(std::ios_base::binary);
2225
2226         writeU16(os, TOSERVER_DAMAGE);
2227         writeU8(os, damage);
2228
2229         // Make data buffer
2230         std::string s = os.str();
2231         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2232         // Send as reliable
2233         Send(0, data, true);
2234 }
2235
2236 void Client::sendBreath(u16 breath)
2237 {
2238         DSTACK(__FUNCTION_NAME);
2239         std::ostringstream os(std::ios_base::binary);
2240
2241         writeU16(os, TOSERVER_BREATH);
2242         writeU16(os, breath);
2243         // Make data buffer
2244         std::string s = os.str();
2245         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2246         // Send as reliable
2247         Send(0, data, true);
2248 }
2249
2250 void Client::sendRespawn()
2251 {
2252         DSTACK(__FUNCTION_NAME);
2253         std::ostringstream os(std::ios_base::binary);
2254
2255         writeU16(os, TOSERVER_RESPAWN);
2256
2257         // Make data buffer
2258         std::string s = os.str();
2259         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2260         // Send as reliable
2261         Send(0, data, true);
2262 }
2263
2264 void Client::sendPlayerPos()
2265 {
2266         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2267         
2268         LocalPlayer *myplayer = m_env.getLocalPlayer();
2269         if(myplayer == NULL)
2270                 return;
2271
2272         // Save bandwidth by only updating position when something changed
2273         if(myplayer->last_position == myplayer->getPosition() &&
2274                         myplayer->last_speed == myplayer->getSpeed() &&
2275                         myplayer->last_pitch == myplayer->getPitch() &&
2276                         myplayer->last_yaw == myplayer->getYaw() &&
2277                         myplayer->last_keyPressed == myplayer->keyPressed)
2278                 return;
2279
2280         myplayer->last_position = myplayer->getPosition();
2281         myplayer->last_speed = myplayer->getSpeed();
2282         myplayer->last_pitch = myplayer->getPitch();
2283         myplayer->last_yaw = myplayer->getYaw();
2284         myplayer->last_keyPressed = myplayer->keyPressed;
2285
2286         u16 our_peer_id;
2287         {
2288                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2289                 our_peer_id = m_con.GetPeerID();
2290         }
2291         
2292         // Set peer id if not set already
2293         if(myplayer->peer_id == PEER_ID_INEXISTENT)
2294                 myplayer->peer_id = our_peer_id;
2295         // Check that an existing peer_id is the same as the connection's
2296         assert(myplayer->peer_id == our_peer_id);
2297         
2298         v3f pf = myplayer->getPosition();
2299         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2300         v3f sf = myplayer->getSpeed();
2301         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2302         s32 pitch = myplayer->getPitch() * 100;
2303         s32 yaw = myplayer->getYaw() * 100;
2304         u32 keyPressed=myplayer->keyPressed;
2305         /*
2306                 Format:
2307                 [0] u16 command
2308                 [2] v3s32 position*100
2309                 [2+12] v3s32 speed*100
2310                 [2+12+12] s32 pitch*100
2311                 [2+12+12+4] s32 yaw*100
2312                 [2+12+12+4+4] u32 keyPressed
2313         */
2314         SharedBuffer<u8> data(2+12+12+4+4+4);
2315         writeU16(&data[0], TOSERVER_PLAYERPOS);
2316         writeV3S32(&data[2], position);
2317         writeV3S32(&data[2+12], speed);
2318         writeS32(&data[2+12+12], pitch);
2319         writeS32(&data[2+12+12+4], yaw);
2320         writeU32(&data[2+12+12+4+4], keyPressed);
2321         // Send as unreliable
2322         Send(0, data, false);
2323 }
2324
2325 void Client::sendPlayerItem(u16 item)
2326 {
2327         Player *myplayer = m_env.getLocalPlayer();
2328         if(myplayer == NULL)
2329                 return;
2330
2331         u16 our_peer_id = m_con.GetPeerID();
2332
2333         // Set peer id if not set already
2334         if(myplayer->peer_id == PEER_ID_INEXISTENT)
2335                 myplayer->peer_id = our_peer_id;
2336         // Check that an existing peer_id is the same as the connection's
2337         assert(myplayer->peer_id == our_peer_id);
2338
2339         SharedBuffer<u8> data(2+2);
2340         writeU16(&data[0], TOSERVER_PLAYERITEM);
2341         writeU16(&data[2], item);
2342
2343         // Send as reliable
2344         Send(0, data, true);
2345 }
2346
2347 void Client::removeNode(v3s16 p)
2348 {
2349         std::map<v3s16, MapBlock*> modified_blocks;
2350
2351         try
2352         {
2353                 //TimeTaker t("removeNodeAndUpdate", m_device);
2354                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2355         }
2356         catch(InvalidPositionException &e)
2357         {
2358         }
2359         
2360         // add urgent task to update the modified node
2361         addUpdateMeshTaskForNode(p, false, true);
2362
2363         for(std::map<v3s16, MapBlock * >::iterator
2364                         i = modified_blocks.begin();
2365                         i != modified_blocks.end(); ++i)
2366         {
2367                 addUpdateMeshTaskWithEdge(i->first);
2368         }
2369 }
2370
2371 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2372 {
2373         TimeTaker timer1("Client::addNode()");
2374
2375         std::map<v3s16, MapBlock*> modified_blocks;
2376
2377         try
2378         {
2379                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2380                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2381         }
2382         catch(InvalidPositionException &e)
2383         {}
2384         
2385         for(std::map<v3s16, MapBlock * >::iterator
2386                         i = modified_blocks.begin();
2387                         i != modified_blocks.end(); ++i)
2388         {
2389                 addUpdateMeshTaskWithEdge(i->first);
2390         }
2391 }
2392         
2393 void Client::setPlayerControl(PlayerControl &control)
2394 {
2395         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2396         LocalPlayer *player = m_env.getLocalPlayer();
2397         assert(player != NULL);
2398         player->control = control;
2399 }
2400
2401 void Client::selectPlayerItem(u16 item)
2402 {
2403         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2404         m_playeritem = item;
2405         m_inventory_updated = true;
2406         sendPlayerItem(item);
2407 }
2408
2409 // Returns true if the inventory of the local player has been
2410 // updated from the server. If it is true, it is set to false.
2411 bool Client::getLocalInventoryUpdated()
2412 {
2413         // m_inventory_updated is behind envlock
2414         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2415         bool updated = m_inventory_updated;
2416         m_inventory_updated = false;
2417         return updated;
2418 }
2419
2420 // Copies the inventory of the local player to parameter
2421 void Client::getLocalInventory(Inventory &dst)
2422 {
2423         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2424         Player *player = m_env.getLocalPlayer();
2425         assert(player != NULL);
2426         dst = player->inventory;
2427 }
2428
2429 Inventory* Client::getInventory(const InventoryLocation &loc)
2430 {
2431         switch(loc.type){
2432         case InventoryLocation::UNDEFINED:
2433         {}
2434         break;
2435         case InventoryLocation::CURRENT_PLAYER:
2436         {
2437                 Player *player = m_env.getLocalPlayer();
2438                 assert(player != NULL);
2439                 return &player->inventory;
2440         }
2441         break;
2442         case InventoryLocation::PLAYER:
2443         {
2444                 Player *player = m_env.getPlayer(loc.name.c_str());
2445                 if(!player)
2446                         return NULL;
2447                 return &player->inventory;
2448         }
2449         break;
2450         case InventoryLocation::NODEMETA:
2451         {
2452                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2453                 if(!meta)
2454                         return NULL;
2455                 return meta->getInventory();
2456         }
2457         break;
2458         case InventoryLocation::DETACHED:
2459         {
2460                 if(m_detached_inventories.count(loc.name) == 0)
2461                         return NULL;
2462                 return m_detached_inventories[loc.name];
2463         }
2464         break;
2465         default:
2466                 assert(0);
2467         }
2468         return NULL;
2469 }
2470 void Client::inventoryAction(InventoryAction *a)
2471 {
2472         /*
2473                 Send it to the server
2474         */
2475         sendInventoryAction(a);
2476
2477         /*
2478                 Predict some local inventory changes
2479         */
2480         a->clientApply(this, this);
2481
2482         // Remove it
2483         delete a;
2484 }
2485
2486 ClientActiveObject * Client::getSelectedActiveObject(
2487                 f32 max_d,
2488                 v3f from_pos_f_on_map,
2489                 core::line3d<f32> shootline_on_map
2490         )
2491 {
2492         std::vector<DistanceSortedActiveObject> objects;
2493
2494         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2495
2496         //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2497         
2498         // Sort them.
2499         // After this, the closest object is the first in the array.
2500         std::sort(objects.begin(), objects.end());
2501
2502         for(u32 i=0; i<objects.size(); i++)
2503         {
2504                 ClientActiveObject *obj = objects[i].obj;
2505                 
2506                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2507                 if(selection_box == NULL)
2508                         continue;
2509
2510                 v3f pos = obj->getPosition();
2511
2512                 core::aabbox3d<f32> offsetted_box(
2513                                 selection_box->MinEdge + pos,
2514                                 selection_box->MaxEdge + pos
2515                 );
2516
2517                 if(offsetted_box.intersectsWithLine(shootline_on_map))
2518                 {
2519                         //infostream<<"Returning selected object"<<std::endl;
2520                         return obj;
2521                 }
2522         }
2523
2524         //infostream<<"No object selected; returning NULL."<<std::endl;
2525         return NULL;
2526 }
2527
2528 void Client::printDebugInfo(std::ostream &os)
2529 {
2530         //JMutexAutoLock lock1(m_fetchblock_mutex);
2531         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2532
2533         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2534                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2535                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2536                 <<std::endl;*/
2537 }
2538
2539 std::list<std::string> Client::getConnectedPlayerNames()
2540 {
2541         return m_env.getPlayerNames();
2542 }
2543
2544 float Client::getAnimationTime()
2545 {
2546         return m_animation_time;
2547 }
2548
2549 int Client::getCrackLevel()
2550 {
2551         return m_crack_level;
2552 }
2553
2554 void Client::setCrack(int level, v3s16 pos)
2555 {
2556         int old_crack_level = m_crack_level;
2557         v3s16 old_crack_pos = m_crack_pos;
2558
2559         m_crack_level = level;
2560         m_crack_pos = pos;
2561
2562         if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2563         {
2564                 // remove old crack
2565                 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2566         }
2567         if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2568         {
2569                 // add new crack
2570                 addUpdateMeshTaskForNode(pos, false, true);
2571         }
2572 }
2573
2574 u16 Client::getHP()
2575 {
2576         Player *player = m_env.getLocalPlayer();
2577         assert(player != NULL);
2578         return player->hp;
2579 }
2580
2581 u16 Client::getBreath()
2582 {
2583         Player *player = m_env.getLocalPlayer();
2584         assert(player != NULL);
2585         return player->getBreath();
2586 }
2587
2588 bool Client::getChatMessage(std::wstring &message)
2589 {
2590         if(m_chat_queue.size() == 0)
2591                 return false;
2592         message = m_chat_queue.pop_front();
2593         return true;
2594 }
2595
2596 void Client::typeChatMessage(const std::wstring &message)
2597 {
2598         // Discard empty line
2599         if(message == L"")
2600                 return;
2601
2602         // Send to others
2603         sendChatMessage(message);
2604
2605         // Show locally
2606         if (message[0] == L'/')
2607         {
2608                 m_chat_queue.push_back(
2609                                 (std::wstring)L"issued command: "+message);
2610         }
2611         else
2612         {
2613                 LocalPlayer *player = m_env.getLocalPlayer();
2614                 assert(player != NULL);
2615                 std::wstring name = narrow_to_wide(player->getName());
2616                 m_chat_queue.push_back(
2617                                 (std::wstring)L"<"+name+L"> "+message);
2618         }
2619 }
2620
2621 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2622 {
2623         /*infostream<<"Client::addUpdateMeshTask(): "
2624                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2625                         <<" ack_to_server="<<ack_to_server
2626                         <<" urgent="<<urgent
2627                         <<std::endl;*/
2628
2629         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2630         if(b == NULL)
2631                 return;
2632         
2633         /*
2634                 Create a task to update the mesh of the block
2635         */
2636         
2637         MeshMakeData *data = new MeshMakeData(this);
2638         
2639         {
2640                 //TimeTaker timer("data fill");
2641                 // Release: ~0ms
2642                 // Debug: 1-6ms, avg=2ms
2643                 data->fill(b);
2644                 data->setCrack(m_crack_level, m_crack_pos);
2645                 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2646         }
2647
2648         // Debug wait
2649         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2650         
2651         // Add task to queue
2652         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2653
2654         /*infostream<<"Mesh update input queue size is "
2655                         <<m_mesh_update_thread.m_queue_in.size()
2656                         <<std::endl;*/
2657 }
2658
2659 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2660 {
2661         /*{
2662                 v3s16 p = blockpos;
2663                 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2664                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2665                                 <<std::endl;
2666         }*/
2667
2668         try{
2669                 v3s16 p = blockpos + v3s16(0,0,0);
2670                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2671                 addUpdateMeshTask(p, ack_to_server, urgent);
2672         }
2673         catch(InvalidPositionException &e){}
2674         // Leading edge
2675         for (int i=0;i<6;i++)
2676         {
2677                 try{
2678                         v3s16 p = blockpos + g_6dirs[i];
2679                         addUpdateMeshTask(p, false, urgent);
2680                 }
2681                 catch(InvalidPositionException &e){}
2682         }
2683 }
2684
2685 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2686 {
2687         {
2688                 v3s16 p = nodepos;
2689                 infostream<<"Client::addUpdateMeshTaskForNode(): "
2690                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2691                                 <<std::endl;
2692         }
2693
2694         v3s16 blockpos = getNodeBlockPos(nodepos);
2695         v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2696
2697         try{
2698                 v3s16 p = blockpos + v3s16(0,0,0);
2699                 addUpdateMeshTask(p, ack_to_server, urgent);
2700         }
2701         catch(InvalidPositionException &e){}
2702         // Leading edge
2703         if(nodepos.X == blockpos_relative.X){
2704                 try{
2705                         v3s16 p = blockpos + v3s16(-1,0,0);
2706                         addUpdateMeshTask(p, false, urgent);
2707                 }
2708                 catch(InvalidPositionException &e){}
2709         }
2710         if(nodepos.Y == blockpos_relative.Y){
2711                 try{
2712                         v3s16 p = blockpos + v3s16(0,-1,0);
2713                         addUpdateMeshTask(p, false, urgent);
2714                 }
2715                 catch(InvalidPositionException &e){}
2716         }
2717         if(nodepos.Z == blockpos_relative.Z){
2718                 try{
2719                         v3s16 p = blockpos + v3s16(0,0,-1);
2720                         addUpdateMeshTask(p, false, urgent);
2721                 }
2722                 catch(InvalidPositionException &e){}
2723         }
2724 }
2725
2726 ClientEvent Client::getClientEvent()
2727 {
2728         if(m_client_event_queue.size() == 0)
2729         {
2730                 ClientEvent event;
2731                 event.type = CE_NONE;
2732                 return event;
2733         }
2734         return m_client_event_queue.pop_front();
2735 }
2736
2737 float Client::mediaReceiveProgress()
2738 {
2739         if (m_media_downloader)
2740                 return m_media_downloader->getProgress();
2741         else
2742                 return 1.0; // downloader only exists when not yet done
2743 }
2744
2745 void draw_load_screen(const std::wstring &text,
2746                 IrrlichtDevice* device, gui::IGUIFont* font,
2747                 float dtime=0 ,int percent=0, bool clouds=true);
2748 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2749 {
2750         infostream<<"Client::afterContentReceived() started"<<std::endl;
2751         assert(m_itemdef_received);
2752         assert(m_nodedef_received);
2753         assert(mediaReceived());
2754         
2755         // Rebuild inherited images and recreate textures
2756         infostream<<"- Rebuilding images and textures"<<std::endl;
2757         m_tsrc->rebuildImagesAndTextures();
2758
2759         // Rebuild shaders
2760         infostream<<"- Rebuilding shaders"<<std::endl;
2761         m_shsrc->rebuildShaders();
2762
2763         // Update node aliases
2764         infostream<<"- Updating node aliases"<<std::endl;
2765         m_nodedef->updateAliases(m_itemdef);
2766
2767         // Update node textures
2768         infostream<<"- Updating node textures"<<std::endl;
2769         m_nodedef->updateTextures(m_tsrc);
2770
2771         // Preload item textures and meshes if configured to
2772         if(g_settings->getBool("preload_item_visuals"))
2773         {
2774                 verbosestream<<"Updating item textures and meshes"<<std::endl;
2775                 wchar_t* text = wgettext("Item textures...");
2776                 draw_load_screen(text,device,font,0,0);
2777                 std::set<std::string> names = m_itemdef->getAll();
2778                 size_t size = names.size();
2779                 size_t count = 0;
2780                 int percent = 0;
2781                 for(std::set<std::string>::const_iterator
2782                                 i = names.begin(); i != names.end(); ++i){
2783                         // Asking for these caches the result
2784                         m_itemdef->getInventoryTexture(*i, this);
2785                         m_itemdef->getWieldMesh(*i, this);
2786                         count++;
2787                         percent = count*100/size;
2788                         if (count%50 == 0) // only update every 50 item
2789                                 draw_load_screen(text,device,font,0,percent);
2790                 }
2791                 delete[] text;
2792         }
2793
2794         // Start mesh update thread after setting up content definitions
2795         infostream<<"- Starting mesh update thread"<<std::endl;
2796         m_mesh_update_thread.Start();
2797         
2798         infostream<<"Client::afterContentReceived() done"<<std::endl;
2799 }
2800
2801 float Client::getRTT(void)
2802 {
2803         try{
2804                 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2805         } catch(con::PeerNotFoundException &e){
2806                 return 1337;
2807         }
2808 }
2809
2810 // IGameDef interface
2811 // Under envlock
2812 IItemDefManager* Client::getItemDefManager()
2813 {
2814         return m_itemdef;
2815 }
2816 INodeDefManager* Client::getNodeDefManager()
2817 {
2818         return m_nodedef;
2819 }
2820 ICraftDefManager* Client::getCraftDefManager()
2821 {
2822         return NULL;
2823         //return m_craftdef;
2824 }
2825 ITextureSource* Client::getTextureSource()
2826 {
2827         return m_tsrc;
2828 }
2829 IShaderSource* Client::getShaderSource()
2830 {
2831         return m_shsrc;
2832 }
2833 u16 Client::allocateUnknownNodeId(const std::string &name)
2834 {
2835         errorstream<<"Client::allocateUnknownNodeId(): "
2836                         <<"Client cannot allocate node IDs"<<std::endl;
2837         assert(0);
2838         return CONTENT_IGNORE;
2839 }
2840 ISoundManager* Client::getSoundManager()
2841 {
2842         return m_sound;
2843 }
2844 MtEventManager* Client::getEventManager()
2845 {
2846         return m_event;
2847 }
2848