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