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