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