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