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