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