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