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