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