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