]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client.cpp
Profiler graph
[dragonfireclient.git] / src / client.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
25 #include "main.h"
26 #include <sstream>
27 #include "porting.h"
28 #include "mapsector.h"
29 #include "mapblock_mesh.h"
30 #include "mapblock.h"
31 #include "settings.h"
32 #include "profiler.h"
33 #include "log.h"
34 #include "nodemetadata.h"
35 #include "nodedef.h"
36 #include "itemdef.h"
37 #include <IFileSystem.h>
38 #include "sha1.h"
39 #include "base64.h"
40 #include "clientmap.h"
41
42 static std::string getTextureCacheDir()
43 {
44         return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
45 }
46
47 struct TextureRequest
48 {
49         std::string name;
50
51         TextureRequest(const std::string &name_=""):
52                 name(name_)
53         {}
54 };
55
56 /*
57         QueuedMeshUpdate
58 */
59
60 QueuedMeshUpdate::QueuedMeshUpdate():
61         p(-1337,-1337,-1337),
62         data(NULL),
63         ack_block_to_server(false)
64 {
65 }
66
67 QueuedMeshUpdate::~QueuedMeshUpdate()
68 {
69         if(data)
70                 delete data;
71 }
72
73 /*
74         MeshUpdateQueue
75 */
76         
77 MeshUpdateQueue::MeshUpdateQueue()
78 {
79         m_mutex.Init();
80 }
81
82 MeshUpdateQueue::~MeshUpdateQueue()
83 {
84         JMutexAutoLock lock(m_mutex);
85
86         for(std::vector<QueuedMeshUpdate*>::iterator
87                         i = m_queue.begin();
88                         i != m_queue.end(); i++)
89         {
90                 QueuedMeshUpdate *q = *i;
91                 delete q;
92         }
93 }
94
95 /*
96         peer_id=0 adds with nobody to send to
97 */
98 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
99 {
100         DSTACK(__FUNCTION_NAME);
101
102         assert(data);
103
104         JMutexAutoLock lock(m_mutex);
105
106         if(urgent)
107                 m_urgents.insert(p);
108
109         /*
110                 Find if block is already in queue.
111                 If it is, update the data and quit.
112         */
113         for(std::vector<QueuedMeshUpdate*>::iterator
114                         i = m_queue.begin();
115                         i != m_queue.end(); i++)
116         {
117                 QueuedMeshUpdate *q = *i;
118                 if(q->p == p)
119                 {
120                         if(q->data)
121                                 delete q->data;
122                         q->data = data;
123                         if(ack_block_to_server)
124                                 q->ack_block_to_server = true;
125                         return;
126                 }
127         }
128         
129         /*
130                 Add the block
131         */
132         QueuedMeshUpdate *q = new QueuedMeshUpdate;
133         q->p = p;
134         q->data = data;
135         q->ack_block_to_server = ack_block_to_server;
136         m_queue.push_back(q);
137 }
138
139 // Returned pointer must be deleted
140 // Returns NULL if queue is empty
141 QueuedMeshUpdate * MeshUpdateQueue::pop()
142 {
143         JMutexAutoLock lock(m_mutex);
144
145         bool must_be_urgent = !m_urgents.empty();
146         for(std::vector<QueuedMeshUpdate*>::iterator
147                         i = m_queue.begin();
148                         i != m_queue.end(); i++)
149         {
150                 QueuedMeshUpdate *q = *i;
151                 if(must_be_urgent && m_urgents.count(q->p) == 0)
152                         continue;
153                 m_queue.erase(i);
154                 m_urgents.erase(q->p);
155                 return q;
156         }
157         return NULL;
158 }
159
160 /*
161         MeshUpdateThread
162 */
163
164 void * MeshUpdateThread::Thread()
165 {
166         ThreadStarted();
167
168         log_register_thread("MeshUpdateThread");
169
170         DSTACK(__FUNCTION_NAME);
171         
172         BEGIN_DEBUG_EXCEPTION_HANDLER
173
174         while(getRun())
175         {
176                 /*// Wait for output queue to flush.
177                 // Allow 2 in queue, this makes less frametime jitter.
178                 // Umm actually, there is no much difference
179                 if(m_queue_out.size() >= 2)
180                 {
181                         sleep_ms(3);
182                         continue;
183                 }*/
184
185                 QueuedMeshUpdate *q = m_queue_in.pop();
186                 if(q == NULL)
187                 {
188                         sleep_ms(3);
189                         continue;
190                 }
191
192                 ScopeProfiler sp(g_profiler, "Client: Mesh making");
193
194                 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
195                 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
196                 {
197                         delete mesh_new;
198                         mesh_new = NULL;
199                 }
200
201                 MeshUpdateResult r;
202                 r.p = q->p;
203                 r.mesh = mesh_new;
204                 r.ack_block_to_server = q->ack_block_to_server;
205
206                 /*infostream<<"MeshUpdateThread: Processed "
207                                 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
208                                 <<std::endl;*/
209
210                 m_queue_out.push_back(r);
211
212                 delete q;
213         }
214
215         END_DEBUG_EXCEPTION_HANDLER(errorstream)
216
217         return NULL;
218 }
219
220 Client::Client(
221                 IrrlichtDevice *device,
222                 const char *playername,
223                 std::string password,
224                 MapDrawControl &control,
225                 IWritableTextureSource *tsrc,
226                 IWritableItemDefManager *itemdef,
227                 IWritableNodeDefManager *nodedef
228 ):
229         m_tsrc(tsrc),
230         m_itemdef(itemdef),
231         m_nodedef(nodedef),
232         m_mesh_update_thread(this),
233         m_env(
234                 new ClientMap(this, this, control,
235                         device->getSceneManager()->getRootSceneNode(),
236                         device->getSceneManager(), 666),
237                 device->getSceneManager(),
238                 tsrc, this, device
239         ),
240         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
241         m_device(device),
242         m_server_ser_ver(SER_FMT_VER_INVALID),
243         m_playeritem(0),
244         m_inventory_updated(false),
245         m_inventory_from_server(NULL),
246         m_inventory_from_server_age(0.0),
247         m_animation_time(0),
248         m_crack_level(-1),
249         m_crack_pos(0,0,0),
250         m_map_seed(0),
251         m_password(password),
252         m_access_denied(false),
253         m_texture_receive_progress(0),
254         m_textures_received(false),
255         m_itemdef_received(false),
256         m_nodedef_received(false),
257         m_time_of_day_set(false),
258         m_last_time_of_day_f(-1),
259         m_time_of_day_update_timer(0)
260 {
261         m_packetcounter_timer = 0.0;
262         //m_delete_unused_sectors_timer = 0.0;
263         m_connection_reinit_timer = 0.0;
264         m_avg_rtt_timer = 0.0;
265         m_playerpos_send_timer = 0.0;
266         m_ignore_damage_timer = 0.0;
267
268         // Build main texture atlas, now that the GameDef exists (that is, us)
269         if(g_settings->getBool("enable_texture_atlas"))
270                 m_tsrc->buildMainAtlas(this);
271         else
272                 infostream<<"Not building texture atlas."<<std::endl;
273         
274         /*
275                 Add local player
276         */
277         {
278                 Player *player = new LocalPlayer(this);
279
280                 player->updateName(playername);
281
282                 m_env.addPlayer(player);
283         }
284 }
285
286 Client::~Client()
287 {
288         {
289                 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
290                 m_con.Disconnect();
291         }
292
293         m_mesh_update_thread.setRun(false);
294         while(m_mesh_update_thread.IsRunning())
295                 sleep_ms(100);
296
297         delete m_inventory_from_server;
298 }
299
300 void Client::connect(Address address)
301 {
302         DSTACK(__FUNCTION_NAME);
303         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
304         m_con.SetTimeoutMs(0);
305         m_con.Connect(address);
306 }
307
308 bool Client::connectedAndInitialized()
309 {
310         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
311
312         if(m_con.Connected() == false)
313                 return false;
314         
315         if(m_server_ser_ver == SER_FMT_VER_INVALID)
316                 return false;
317         
318         return true;
319 }
320
321 void Client::step(float dtime)
322 {
323         DSTACK(__FUNCTION_NAME);
324         
325         // Limit a bit
326         if(dtime > 2.0)
327                 dtime = 2.0;
328         
329         if(m_ignore_damage_timer > dtime)
330                 m_ignore_damage_timer -= dtime;
331         else
332                 m_ignore_damage_timer = 0.0;
333         
334         m_animation_time += dtime;
335         if(m_animation_time > 60.0)
336                 m_animation_time -= 60.0;
337
338         m_time_of_day_update_timer += dtime;
339         
340         //infostream<<"Client steps "<<dtime<<std::endl;
341
342         {
343                 //TimeTaker timer("ReceiveAll()", m_device);
344                 // 0ms
345                 ReceiveAll();
346         }
347         
348         {
349                 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
350                 // 0ms
351                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
352                 m_con.RunTimeouts(dtime);
353         }
354
355         /*
356                 Packet counter
357         */
358         {
359                 float &counter = m_packetcounter_timer;
360                 counter -= dtime;
361                 if(counter <= 0.0)
362                 {
363                         counter = 20.0;
364                         
365                         infostream<<"Client packetcounter (20s):"<<std::endl;
366                         m_packetcounter.print(infostream);
367                         m_packetcounter.clear();
368                 }
369         }
370         
371         // Get connection status
372         bool connected = connectedAndInitialized();
373
374 #if 0
375         {
376                 /*
377                         Delete unused sectors
378
379                         NOTE: This jams the game for a while because deleting sectors
380                               clear caches
381                 */
382                 
383                 float &counter = m_delete_unused_sectors_timer;
384                 counter -= dtime;
385                 if(counter <= 0.0)
386                 {
387                         // 3 minute interval
388                         //counter = 180.0;
389                         counter = 60.0;
390
391                         //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
392
393                         core::list<v3s16> deleted_blocks;
394
395                         float delete_unused_sectors_timeout = 
396                                 g_settings->getFloat("client_delete_unused_sectors_timeout");
397         
398                         // Delete sector blocks
399                         /*u32 num = m_env.getMap().unloadUnusedData
400                                         (delete_unused_sectors_timeout,
401                                         true, &deleted_blocks);*/
402                         
403                         // Delete whole sectors
404                         m_env.getMap().unloadUnusedData
405                                         (delete_unused_sectors_timeout,
406                                         &deleted_blocks);
407
408                         if(deleted_blocks.size() > 0)
409                         {
410                                 /*infostream<<"Client: Deleted blocks of "<<num
411                                                 <<" unused sectors"<<std::endl;*/
412                                 /*infostream<<"Client: Deleted "<<num
413                                                 <<" unused sectors"<<std::endl;*/
414                                 
415                                 /*
416                                         Send info to server
417                                 */
418
419                                 // Env is locked so con can be locked.
420                                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
421                                 
422                                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
423                                 core::list<v3s16> sendlist;
424                                 for(;;)
425                                 {
426                                         if(sendlist.size() == 255 || i == deleted_blocks.end())
427                                         {
428                                                 if(sendlist.size() == 0)
429                                                         break;
430                                                 /*
431                                                         [0] u16 command
432                                                         [2] u8 count
433                                                         [3] v3s16 pos_0
434                                                         [3+6] v3s16 pos_1
435                                                         ...
436                                                 */
437                                                 u32 replysize = 2+1+6*sendlist.size();
438                                                 SharedBuffer<u8> reply(replysize);
439                                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
440                                                 reply[2] = sendlist.size();
441                                                 u32 k = 0;
442                                                 for(core::list<v3s16>::Iterator
443                                                                 j = sendlist.begin();
444                                                                 j != sendlist.end(); j++)
445                                                 {
446                                                         writeV3S16(&reply[2+1+6*k], *j);
447                                                         k++;
448                                                 }
449                                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
450
451                                                 if(i == deleted_blocks.end())
452                                                         break;
453
454                                                 sendlist.clear();
455                                         }
456
457                                         sendlist.push_back(*i);
458                                         i++;
459                                 }
460                         }
461                 }
462         }
463 #endif
464
465         if(connected == false)
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         
478                         // Send TOSERVER_INIT
479                         // [0] u16 TOSERVER_INIT
480                         // [2] u8 SER_FMT_VER_HIGHEST
481                         // [3] u8[20] player_name
482                         // [23] u8[28] password (new in some version)
483                         // [51] u16 client network protocol version (new in some version)
484                         SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
485                         writeU16(&data[0], TOSERVER_INIT);
486                         writeU8(&data[2], SER_FMT_VER_HIGHEST);
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                         // This should be incremented in each version
498                         writeU16(&data[51], PROTOCOL_VERSION);
499
500                         // Send as unreliable
501                         Send(0, 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                 core::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                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
534                 core::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(core::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, 1, 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                 // 0ms
578                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
579
580                 // Control local player (0ms)
581                 LocalPlayer *player = m_env.getLocalPlayer();
582                 assert(player != NULL);
583                 player->applyControl(dtime);
584
585                 //TimeTaker envtimer("env step", m_device);
586                 // Step environment
587                 m_env.step(dtime);
588                 
589                 /*
590                         Get events
591                 */
592                 for(;;)
593                 {
594                         ClientEnvEvent event = m_env.getClientEvent();
595                         if(event.type == CEE_NONE)
596                         {
597                                 break;
598                         }
599                         else if(event.type == CEE_PLAYER_DAMAGE)
600                         {
601                                 if(m_ignore_damage_timer <= 0)
602                                 {
603                                         u8 damage = event.player_damage.amount;
604                                         
605                                         if(event.player_damage.send_to_server)
606                                                 sendDamage(damage);
607
608                                         // Add to ClientEvent queue
609                                         ClientEvent event;
610                                         event.type = CE_PLAYER_DAMAGE;
611                                         event.player_damage.amount = damage;
612                                         m_client_event_queue.push_back(event);
613                                 }
614                         }
615                 }
616         }
617         
618         /*
619                 Print some info
620         */
621         {
622                 float &counter = m_avg_rtt_timer;
623                 counter += dtime;
624                 if(counter >= 10)
625                 {
626                         counter = 0.0;
627                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
628                         // connectedAndInitialized() is true, peer exists.
629                         float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
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(counter >= 0.2)
641                 {
642                         counter = 0.0;
643                         sendPlayerPos();
644                 }
645         }
646
647         /*
648                 Replace updated meshes
649         */
650         {
651                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
652
653                 //TimeTaker timer("** Processing mesh update result queue");
654                 // 0ms
655                 
656                 /*infostream<<"Mesh update result queue size is "
657                                 <<m_mesh_update_thread.m_queue_out.size()
658                                 <<std::endl;*/
659                 
660                 int num_processed_meshes = 0;
661                 while(m_mesh_update_thread.m_queue_out.size() > 0)
662                 {
663                         num_processed_meshes++;
664                         MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
665                         MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
666                         if(block)
667                         {
668                                 //JMutexAutoLock lock(block->mesh_mutex);
669
670                                 // Delete the old mesh
671                                 if(block->mesh != NULL)
672                                 {
673                                         // TODO: Remove hardware buffers of meshbuffers of block->mesh
674                                         delete block->mesh;
675                                         block->mesh = NULL;
676                                 }
677
678                                 // Replace with the new mesh
679                                 block->mesh = r.mesh;
680                         }
681                         if(r.ack_block_to_server)
682                         {
683                                 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
684                                                 <<","<<r.p.Z<<")"<<std::endl;*/
685                                 /*
686                                         Acknowledge block
687                                 */
688                                 /*
689                                         [0] u16 command
690                                         [2] u8 count
691                                         [3] v3s16 pos_0
692                                         [3+6] v3s16 pos_1
693                                         ...
694                                 */
695                                 u32 replysize = 2+1+6;
696                                 SharedBuffer<u8> reply(replysize);
697                                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
698                                 reply[2] = 1;
699                                 writeV3S16(&reply[3], r.p);
700                                 // Send as reliable
701                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
702                         }
703                 }
704                 if(num_processed_meshes > 0)
705                         g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
706         }
707
708         /*
709                 If the server didn't update the inventory in a while, revert
710                 the local inventory (so the player notices the lag problem
711                 and knows something is wrong).
712         */
713         if(m_inventory_from_server)
714         {
715                 float interval = 10.0;
716                 float count_before = floor(m_inventory_from_server_age / interval);
717
718                 m_inventory_from_server_age += dtime;
719
720                 float count_after = floor(m_inventory_from_server_age / interval);
721
722                 if(count_after != count_before)
723                 {
724                         // Do this every <interval> seconds after TOCLIENT_INVENTORY
725                         // Reset the locally changed inventory to the authoritative inventory
726                         Player *player = m_env.getLocalPlayer();
727                         player->inventory = *m_inventory_from_server;
728                         m_inventory_updated = true;
729                 }
730         }
731 }
732
733 // Virtual methods from con::PeerHandler
734 void Client::peerAdded(con::Peer *peer)
735 {
736         infostream<<"Client::peerAdded(): peer->id="
737                         <<peer->id<<std::endl;
738 }
739 void Client::deletingPeer(con::Peer *peer, bool timeout)
740 {
741         infostream<<"Client::deletingPeer(): "
742                         "Server Peer is getting deleted "
743                         <<"(timeout="<<timeout<<")"<<std::endl;
744 }
745
746 void Client::ReceiveAll()
747 {
748         DSTACK(__FUNCTION_NAME);
749         u32 start_ms = porting::getTimeMs();
750         for(;;)
751         {
752                 // Limit time even if there would be huge amounts of data to
753                 // process
754                 if(porting::getTimeMs() > start_ms + 100)
755                         break;
756                 
757                 try{
758                         Receive();
759                         g_profiler->graphAdd("client_received_packets", 1);
760                 }
761                 catch(con::NoIncomingDataException &e)
762                 {
763                         break;
764                 }
765                 catch(con::InvalidIncomingDataException &e)
766                 {
767                         infostream<<"Client::ReceiveAll(): "
768                                         "InvalidIncomingDataException: what()="
769                                         <<e.what()<<std::endl;
770                 }
771         }
772 }
773
774 void Client::Receive()
775 {
776         DSTACK(__FUNCTION_NAME);
777         SharedBuffer<u8> data;
778         u16 sender_peer_id;
779         u32 datasize;
780         {
781                 //TimeTaker t1("con mutex and receive", m_device);
782                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
783                 datasize = m_con.Receive(sender_peer_id, data);
784         }
785         //TimeTaker t1("ProcessData", m_device);
786         ProcessData(*data, datasize, sender_peer_id);
787 }
788
789 /*
790         sender_peer_id given to this shall be quaranteed to be a valid peer
791 */
792 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
793 {
794         DSTACK(__FUNCTION_NAME);
795
796         // Ignore packets that don't even fit a command
797         if(datasize < 2)
798         {
799                 m_packetcounter.add(60000);
800                 return;
801         }
802
803         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
804
805         //infostream<<"Client: received command="<<command<<std::endl;
806         m_packetcounter.add((u16)command);
807         
808         /*
809                 If this check is removed, be sure to change the queue
810                 system to know the ids
811         */
812         if(sender_peer_id != PEER_ID_SERVER)
813         {
814                 infostream<<"Client::ProcessData(): Discarding data not "
815                                 "coming from server: peer_id="<<sender_peer_id
816                                 <<std::endl;
817                 return;
818         }
819
820         u8 ser_version = m_server_ser_ver;
821
822         //infostream<<"Client received command="<<(int)command<<std::endl;
823
824         if(command == TOCLIENT_INIT)
825         {
826                 if(datasize < 3)
827                         return;
828
829                 u8 deployed = data[2];
830
831                 infostream<<"Client: TOCLIENT_INIT received with "
832                                 "deployed="<<((int)deployed&0xff)<<std::endl;
833
834                 if(deployed < SER_FMT_VER_LOWEST
835                                 || deployed > SER_FMT_VER_HIGHEST)
836                 {
837                         infostream<<"Client: TOCLIENT_INIT: Server sent "
838                                         <<"unsupported ser_fmt_ver"<<std::endl;
839                         return;
840                 }
841                 
842                 m_server_ser_ver = deployed;
843
844                 // Get player position
845                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
846                 if(datasize >= 2+1+6)
847                         playerpos_s16 = readV3S16(&data[2+1]);
848                 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
849
850                 { //envlock
851                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
852                         
853                         // Set player position
854                         Player *player = m_env.getLocalPlayer();
855                         assert(player != NULL);
856                         player->setPosition(playerpos_f);
857                 }
858                 
859                 if(datasize >= 2+1+6+8)
860                 {
861                         // Get map seed
862                         m_map_seed = readU64(&data[2+1+6]);
863                         infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
864                 }
865                 
866                 // Reply to server
867                 u32 replysize = 2;
868                 SharedBuffer<u8> reply(replysize);
869                 writeU16(&reply[0], TOSERVER_INIT2);
870                 // Send as reliable
871                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
872
873                 return;
874         }
875
876         if(command == TOCLIENT_ACCESS_DENIED)
877         {
878                 // The server didn't like our password. Note, this needs
879                 // to be processed even if the serialisation format has
880                 // not been agreed yet, the same as TOCLIENT_INIT.
881                 m_access_denied = true;
882                 m_access_denied_reason = L"Unknown";
883                 if(datasize >= 4)
884                 {
885                         std::string datastring((char*)&data[2], datasize-2);
886                         std::istringstream is(datastring, std::ios_base::binary);
887                         m_access_denied_reason = deSerializeWideString(is);
888                 }
889                 return;
890         }
891
892         if(ser_version == SER_FMT_VER_INVALID)
893         {
894                 infostream<<"Client: Server serialization"
895                                 " format invalid or not initialized."
896                                 " Skipping incoming command="<<command<<std::endl;
897                 return;
898         }
899         
900         // Just here to avoid putting the two if's together when
901         // making some copypasta
902         {}
903
904         if(command == TOCLIENT_REMOVENODE)
905         {
906                 if(datasize < 8)
907                         return;
908                 v3s16 p;
909                 p.X = readS16(&data[2]);
910                 p.Y = readS16(&data[4]);
911                 p.Z = readS16(&data[6]);
912                 
913                 //TimeTaker t1("TOCLIENT_REMOVENODE");
914                 
915                 removeNode(p);
916         }
917         else if(command == TOCLIENT_ADDNODE)
918         {
919                 if(datasize < 8 + MapNode::serializedLength(ser_version))
920                         return;
921
922                 v3s16 p;
923                 p.X = readS16(&data[2]);
924                 p.Y = readS16(&data[4]);
925                 p.Z = readS16(&data[6]);
926                 
927                 //TimeTaker t1("TOCLIENT_ADDNODE");
928
929                 MapNode n;
930                 n.deSerialize(&data[8], ser_version);
931                 
932                 addNode(p, n);
933         }
934         else if(command == TOCLIENT_BLOCKDATA)
935         {
936                 // Ignore too small packet
937                 if(datasize < 8)
938                         return;
939                         
940                 v3s16 p;
941                 p.X = readS16(&data[2]);
942                 p.Y = readS16(&data[4]);
943                 p.Z = readS16(&data[6]);
944                 
945                 /*infostream<<"Client: Thread: BLOCKDATA for ("
946                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
947                 /*infostream<<"Client: Thread: BLOCKDATA for ("
948                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
949                 
950                 std::string datastring((char*)&data[8], datasize-8);
951                 std::istringstream istr(datastring, std::ios_base::binary);
952                 
953                 MapSector *sector;
954                 MapBlock *block;
955                 
956                 v2s16 p2d(p.X, p.Z);
957                 sector = m_env.getMap().emergeSector(p2d);
958                 
959                 assert(sector->getPos() == p2d);
960
961                 //TimeTaker timer("MapBlock deSerialize");
962                 // 0ms
963                 
964                 block = sector->getBlockNoCreateNoEx(p.Y);
965                 if(block)
966                 {
967                         /*
968                                 Update an existing block
969                         */
970                         //infostream<<"Updating"<<std::endl;
971                         block->deSerialize(istr, ser_version, false);
972                 }
973                 else
974                 {
975                         /*
976                                 Create a new block
977                         */
978                         //infostream<<"Creating new"<<std::endl;
979                         block = new MapBlock(&m_env.getMap(), p, this);
980                         block->deSerialize(istr, ser_version, false);
981                         sector->insertBlock(block);
982                 }
983
984 #if 0
985                 /*
986                         Acknowledge block
987                 */
988                 /*
989                         [0] u16 command
990                         [2] u8 count
991                         [3] v3s16 pos_0
992                         [3+6] v3s16 pos_1
993                         ...
994                 */
995                 u32 replysize = 2+1+6;
996                 SharedBuffer<u8> reply(replysize);
997                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
998                 reply[2] = 1;
999                 writeV3S16(&reply[3], p);
1000                 // Send as reliable
1001                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1002 #endif
1003
1004                 /*
1005                         Add it to mesh update queue and set it to be acknowledged after update.
1006                 */
1007                 //infostream<<"Adding mesh update task for received block"<<std::endl;
1008                 addUpdateMeshTaskWithEdge(p, true);
1009         }
1010         else if(command == TOCLIENT_INVENTORY)
1011         {
1012                 if(datasize < 3)
1013                         return;
1014
1015                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1016
1017                 { //envlock
1018                         //TimeTaker t2("mutex locking", m_device);
1019                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1020                         //t2.stop();
1021                         
1022                         //TimeTaker t3("istringstream init", m_device);
1023                         std::string datastring((char*)&data[2], datasize-2);
1024                         std::istringstream is(datastring, std::ios_base::binary);
1025                         //t3.stop();
1026                         
1027                         //m_env.printPlayers(infostream);
1028
1029                         //TimeTaker t4("player get", m_device);
1030                         Player *player = m_env.getLocalPlayer();
1031                         assert(player != NULL);
1032                         //t4.stop();
1033
1034                         //TimeTaker t1("inventory.deSerialize()", m_device);
1035                         player->inventory.deSerialize(is);
1036                         //t1.stop();
1037
1038                         m_inventory_updated = true;
1039
1040                         delete m_inventory_from_server;
1041                         m_inventory_from_server = new Inventory(player->inventory);
1042                         m_inventory_from_server_age = 0.0;
1043
1044                         //infostream<<"Client got player inventory:"<<std::endl;
1045                         //player->inventory.print(infostream);
1046                 }
1047         }
1048         else if(command == TOCLIENT_TIME_OF_DAY)
1049         {
1050                 if(datasize < 4)
1051                         return;
1052                 
1053                 u16 time_of_day = readU16(&data[2]);
1054                 time_of_day = time_of_day % 24000;
1055                 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1056                 float time_speed = 0;
1057                 if(datasize >= 2 + 2 + 4){
1058                         time_speed = readF1000(&data[4]);
1059                 } else {
1060                         // Old message; try to approximate speed of time by ourselves
1061                         float time_of_day_f = (float)time_of_day / 24000.0;
1062                         float tod_diff_f = 0;
1063                         if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1064                                 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1065                         else
1066                                 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1067                         m_last_time_of_day_f = time_of_day_f;
1068                         float time_diff = m_time_of_day_update_timer;
1069                         m_time_of_day_update_timer = 0;
1070                         if(m_time_of_day_set){
1071                                 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1072                                 infostream<<"Client: Measured time_of_day speed (old format): "
1073                                                 <<time_speed<<" tod_diff_f="<<tod_diff_f
1074                                                 <<" time_diff="<<time_diff<<std::endl;
1075                         }
1076                 }
1077                 
1078                 // Update environment
1079                 m_env.setTimeOfDay(time_of_day);
1080                 m_env.setTimeOfDaySpeed(time_speed);
1081                 m_time_of_day_set = true;
1082
1083                 u32 dr = m_env.getDayNightRatio();
1084                 verbosestream<<"Client: time_of_day="<<time_of_day
1085                                 <<" time_speed="<<time_speed
1086                                 <<" dr="<<dr<<std::endl;
1087         }
1088         else if(command == TOCLIENT_CHAT_MESSAGE)
1089         {
1090                 /*
1091                         u16 command
1092                         u16 length
1093                         wstring message
1094                 */
1095                 u8 buf[6];
1096                 std::string datastring((char*)&data[2], datasize-2);
1097                 std::istringstream is(datastring, std::ios_base::binary);
1098                 
1099                 // Read stuff
1100                 is.read((char*)buf, 2);
1101                 u16 len = readU16(buf);
1102                 
1103                 std::wstring message;
1104                 for(u16 i=0; i<len; i++)
1105                 {
1106                         is.read((char*)buf, 2);
1107                         message += (wchar_t)readU16(buf);
1108                 }
1109
1110                 /*infostream<<"Client received chat message: "
1111                                 <<wide_to_narrow(message)<<std::endl;*/
1112                 
1113                 m_chat_queue.push_back(message);
1114         }
1115         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1116         {
1117                 //if(g_settings->getBool("enable_experimental"))
1118                 {
1119                         /*
1120                                 u16 command
1121                                 u16 count of removed objects
1122                                 for all removed objects {
1123                                         u16 id
1124                                 }
1125                                 u16 count of added objects
1126                                 for all added objects {
1127                                         u16 id
1128                                         u8 type
1129                                         u32 initialization data length
1130                                         string initialization data
1131                                 }
1132                         */
1133
1134                         char buf[6];
1135                         // Get all data except the command number
1136                         std::string datastring((char*)&data[2], datasize-2);
1137                         // Throw them in an istringstream
1138                         std::istringstream is(datastring, std::ios_base::binary);
1139
1140                         // Read stuff
1141                         
1142                         // Read removed objects
1143                         is.read(buf, 2);
1144                         u16 removed_count = readU16((u8*)buf);
1145                         for(u16 i=0; i<removed_count; i++)
1146                         {
1147                                 is.read(buf, 2);
1148                                 u16 id = readU16((u8*)buf);
1149                                 // Remove it
1150                                 {
1151                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1152                                         m_env.removeActiveObject(id);
1153                                 }
1154                         }
1155                         
1156                         // Read added objects
1157                         is.read(buf, 2);
1158                         u16 added_count = readU16((u8*)buf);
1159                         for(u16 i=0; i<added_count; i++)
1160                         {
1161                                 is.read(buf, 2);
1162                                 u16 id = readU16((u8*)buf);
1163                                 is.read(buf, 1);
1164                                 u8 type = readU8((u8*)buf);
1165                                 std::string data = deSerializeLongString(is);
1166                                 // Add it
1167                                 {
1168                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1169                                         m_env.addActiveObject(id, type, data);
1170                                 }
1171                         }
1172                 }
1173         }
1174         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1175         {
1176                 //if(g_settings->getBool("enable_experimental"))
1177                 {
1178                         /*
1179                                 u16 command
1180                                 for all objects
1181                                 {
1182                                         u16 id
1183                                         u16 message length
1184                                         string message
1185                                 }
1186                         */
1187                         char buf[6];
1188                         // Get all data except the command number
1189                         std::string datastring((char*)&data[2], datasize-2);
1190                         // Throw them in an istringstream
1191                         std::istringstream is(datastring, std::ios_base::binary);
1192                         
1193                         while(is.eof() == false)
1194                         {
1195                                 // Read stuff
1196                                 is.read(buf, 2);
1197                                 u16 id = readU16((u8*)buf);
1198                                 if(is.eof())
1199                                         break;
1200                                 is.read(buf, 2);
1201                                 u16 message_size = readU16((u8*)buf);
1202                                 std::string message;
1203                                 message.reserve(message_size);
1204                                 for(u16 i=0; i<message_size; i++)
1205                                 {
1206                                         is.read(buf, 1);
1207                                         message.append(buf, 1);
1208                                 }
1209                                 // Pass on to the environment
1210                                 {
1211                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1212                                         m_env.processActiveObjectMessage(id, message);
1213                                 }
1214                         }
1215                 }
1216         }
1217         else if(command == TOCLIENT_HP)
1218         {
1219                 std::string datastring((char*)&data[2], datasize-2);
1220                 std::istringstream is(datastring, std::ios_base::binary);
1221                 Player *player = m_env.getLocalPlayer();
1222                 assert(player != NULL);
1223                 u8 oldhp = player->hp;
1224                 u8 hp = readU8(is);
1225                 player->hp = hp;
1226
1227                 if(hp < oldhp)
1228                 {
1229                         // Add to ClientEvent queue
1230                         ClientEvent event;
1231                         event.type = CE_PLAYER_DAMAGE;
1232                         event.player_damage.amount = oldhp - hp;
1233                         m_client_event_queue.push_back(event);
1234                 }
1235         }
1236         else if(command == TOCLIENT_MOVE_PLAYER)
1237         {
1238                 std::string datastring((char*)&data[2], datasize-2);
1239                 std::istringstream is(datastring, std::ios_base::binary);
1240                 Player *player = m_env.getLocalPlayer();
1241                 assert(player != NULL);
1242                 v3f pos = readV3F1000(is);
1243                 f32 pitch = readF1000(is);
1244                 f32 yaw = readF1000(is);
1245                 player->setPosition(pos);
1246                 /*player->setPitch(pitch);
1247                 player->setYaw(yaw);*/
1248
1249                 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1250                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1251                                 <<" pitch="<<pitch
1252                                 <<" yaw="<<yaw
1253                                 <<std::endl;
1254
1255                 /*
1256                         Add to ClientEvent queue.
1257                         This has to be sent to the main program because otherwise
1258                         it would just force the pitch and yaw values to whatever
1259                         the camera points to.
1260                 */
1261                 ClientEvent event;
1262                 event.type = CE_PLAYER_FORCE_MOVE;
1263                 event.player_force_move.pitch = pitch;
1264                 event.player_force_move.yaw = yaw;
1265                 m_client_event_queue.push_back(event);
1266
1267                 // Ignore damage for a few seconds, so that the player doesn't
1268                 // get damage from falling on ground
1269                 m_ignore_damage_timer = 3.0;
1270         }
1271         else if(command == TOCLIENT_PLAYERITEM)
1272         {
1273                 std::string datastring((char*)&data[2], datasize-2);
1274                 std::istringstream is(datastring, std::ios_base::binary);
1275
1276                 u16 count = readU16(is);
1277
1278                 for (u16 i = 0; i < count; ++i) {
1279                         u16 peer_id = readU16(is);
1280                         Player *player = m_env.getPlayer(peer_id);
1281
1282                         if (player == NULL)
1283                         {
1284                                 infostream<<"Client: ignoring player item "
1285                                         << deSerializeString(is)
1286                                         << " for non-existing peer id " << peer_id
1287                                         << std::endl;
1288                                 continue;
1289                         } else if (player->isLocal()) {
1290                                 infostream<<"Client: ignoring player item "
1291                                         << deSerializeString(is)
1292                                         << " for local player" << std::endl;
1293                                 continue;
1294                         } else {
1295                                 InventoryList *inv = player->inventory.getList("main");
1296                                 std::string itemstring(deSerializeString(is));
1297                                 ItemStack item;
1298                                 item.deSerialize(itemstring, m_itemdef);
1299                                 inv->changeItem(0, item);
1300                                 if(itemstring.empty())
1301                                 {
1302                                         infostream<<"Client: empty player item for peer "
1303                                                 <<peer_id<<std::endl;
1304                                 }
1305                                 else
1306                                 {
1307                                         infostream<<"Client: player item for peer "
1308                                                 <<peer_id<<": "<<itemstring<<std::endl;
1309                                 }
1310                         }
1311                 }
1312         }
1313         else if(command == TOCLIENT_DEATHSCREEN)
1314         {
1315                 std::string datastring((char*)&data[2], datasize-2);
1316                 std::istringstream is(datastring, std::ios_base::binary);
1317                 
1318                 bool set_camera_point_target = readU8(is);
1319                 v3f camera_point_target = readV3F1000(is);
1320                 
1321                 ClientEvent event;
1322                 event.type = CE_DEATHSCREEN;
1323                 event.deathscreen.set_camera_point_target = set_camera_point_target;
1324                 event.deathscreen.camera_point_target_x = camera_point_target.X;
1325                 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1326                 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1327                 m_client_event_queue.push_back(event);
1328         }
1329         else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
1330         {
1331                 io::IFileSystem *irrfs = m_device->getFileSystem();
1332                 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1333
1334                 std::string datastring((char*)&data[2], datasize-2);
1335                 std::istringstream is(datastring, std::ios_base::binary);
1336
1337                 // Mesh update thread must be stopped while
1338                 // updating content definitions
1339                 assert(!m_mesh_update_thread.IsRunning());
1340
1341                 int num_textures = readU16(is);
1342
1343                 core::list<TextureRequest> texture_requests;
1344
1345                 for(int i=0; i<num_textures; i++){
1346
1347                         bool texture_found = false;
1348
1349                         //read texture from cache
1350                         std::string name = deSerializeString(is);
1351                         std::string sha1_texture = deSerializeString(is);
1352                         
1353                         // if name contains illegal characters, ignore the texture
1354                         if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1355                                 errorstream<<"Client: ignoring illegal texture name "
1356                                                 <<"sent by server: \""<<name<<"\""<<std::endl;
1357                                 continue;
1358                         }
1359
1360                         std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
1361                         // Read data
1362                         std::ifstream fis(tpath.c_str(), std::ios_base::binary);
1363
1364
1365                         if(fis.good() == false){
1366                                 infostream<<"Client::Texture not found in cache: "
1367                                                 <<name << " expected it at: "<<tpath<<std::endl;
1368                         }
1369                         else
1370                         {
1371                                 std::ostringstream tmp_os(std::ios_base::binary);
1372                                 bool bad = false;
1373                                 for(;;){
1374                                         char buf[1024];
1375                                         fis.read(buf, 1024);
1376                                         std::streamsize len = fis.gcount();
1377                                         tmp_os.write(buf, len);
1378                                         if(fis.eof())
1379                                                 break;
1380                                         if(!fis.good()){
1381                                                 bad = true;
1382                                                 break;
1383                                         }
1384                                 }
1385                                 if(bad){
1386                                         infostream<<"Client: Failed to read texture from cache\""
1387                                                         <<name<<"\""<<std::endl;
1388                                 }
1389                                 else {
1390
1391                                         SHA1 sha1;
1392                                         sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
1393
1394                                         unsigned char *digest = sha1.getDigest();
1395
1396                                         std::string digest_string = base64_encode(digest, 20);
1397
1398                                         if (digest_string == sha1_texture) {
1399                                                 // Silly irrlicht's const-incorrectness
1400                                                 Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
1401
1402                                                 // Create an irrlicht memory file
1403                                                 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1404                                                                 *data_rw,  tmp_os.str().size(), "_tempreadfile");
1405                                                 assert(rfile);
1406                                                 // Read image
1407                                                 video::IImage *img = vdrv->createImageFromFile(rfile);
1408                                                 if(!img){
1409                                                         infostream<<"Client: Cannot create image from data of "
1410                                                                         <<"received texture \""<<name<<"\""<<std::endl;
1411                                                         rfile->drop();
1412                                                 }
1413                                                 else {
1414                                                         m_tsrc->insertSourceImage(name, img);
1415                                                         img->drop();
1416                                                         rfile->drop();
1417
1418                                                         texture_found = true;
1419                                                 }
1420                                         }
1421                                         else {
1422                                                 infostream<<"Client::Texture cached sha1 hash not matching server hash: "
1423                                                                 <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
1424                                         }
1425
1426                                         free(digest);
1427                                 }
1428                         }
1429
1430                         //add texture request
1431                         if (!texture_found) {
1432                                 infostream<<"Client: Adding texture to request list: \""
1433                                                 <<name<<"\""<<std::endl;
1434                                 texture_requests.push_back(TextureRequest(name));
1435                         }
1436
1437                 }
1438
1439                 ClientEvent event;
1440                 event.type = CE_TEXTURES_UPDATED;
1441                 m_client_event_queue.push_back(event);
1442
1443
1444                 //send Texture request
1445                 /*
1446                                 u16 command
1447                                 u16 number of textures requested
1448                                 for each texture {
1449                                         u16 length of name
1450                                         string name
1451                                 }
1452                  */
1453                 std::ostringstream os(std::ios_base::binary);
1454                 u8 buf[12];
1455
1456
1457                 // Write command
1458                 writeU16(buf, TOSERVER_REQUEST_TEXTURES);
1459                 os.write((char*)buf, 2);
1460
1461                 writeU16(buf,texture_requests.size());
1462                 os.write((char*)buf, 2);
1463
1464
1465                 for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
1466                                 i != texture_requests.end(); i++) {
1467                         os<<serializeString(i->name);
1468                 }
1469
1470                 // Make data buffer
1471                 std::string s = os.str();
1472                 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1473                 // Send as reliable
1474                 Send(0, data, true);
1475                 infostream<<"Client: Sending request list to server " <<std::endl;
1476         }
1477         else if(command == TOCLIENT_TEXTURES)
1478         {
1479                 io::IFileSystem *irrfs = m_device->getFileSystem();
1480                 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1481
1482                 std::string datastring((char*)&data[2], datasize-2);
1483                 std::istringstream is(datastring, std::ios_base::binary);
1484
1485                 // Mesh update thread must be stopped while
1486                 // updating content definitions
1487                 assert(!m_mesh_update_thread.IsRunning());
1488
1489                 /*
1490                         u16 command
1491                         u16 total number of texture bunches
1492                         u16 index of this bunch
1493                         u32 number of textures in this bunch
1494                         for each texture {
1495                                 u16 length of name
1496                                 string name
1497                                 u32 length of data
1498                                 data
1499                         }
1500                 */
1501                 int num_bunches = readU16(is);
1502                 int bunch_i = readU16(is);
1503                 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1504                 if(bunch_i == num_bunches - 1)
1505                         m_textures_received = true;
1506                 int num_textures = readU32(is);
1507                 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1508                                 <<num_bunches<<" textures="<<num_textures
1509                                 <<" size="<<datasize<<std::endl;
1510                 for(int i=0; i<num_textures; i++){
1511                         std::string name = deSerializeString(is);
1512                         std::string data = deSerializeLongString(is);
1513
1514                         // if name contains illegal characters, ignore the texture
1515                         if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1516                                 errorstream<<"Client: ignoring illegal texture name "
1517                                                 <<"sent by server: \""<<name<<"\""<<std::endl;
1518                                 continue;
1519                         }
1520
1521                         // Silly irrlicht's const-incorrectness
1522                         Buffer<char> data_rw(data.c_str(), data.size());
1523                         // Create an irrlicht memory file
1524                         io::IReadFile *rfile = irrfs->createMemoryReadFile(
1525                                         *data_rw, data.size(), "_tempreadfile");
1526                         assert(rfile);
1527                         // Read image
1528                         video::IImage *img = vdrv->createImageFromFile(rfile);
1529                         if(!img){
1530                                 errorstream<<"Client: Cannot create image from data of "
1531                                                 <<"received texture \""<<name<<"\""<<std::endl;
1532                                 rfile->drop();
1533                                 continue;
1534                         }
1535
1536                         fs::CreateAllDirs(getTextureCacheDir());
1537
1538                         std::string filename = getTextureCacheDir() + DIR_DELIM + name;
1539                         std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
1540
1541                         if (outfile.good()) {
1542                                 outfile.write(data.c_str(),data.length());
1543                                 outfile.close();
1544                         }
1545                         else {
1546                                 errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
1547                         }
1548
1549                         m_tsrc->insertSourceImage(name, img);
1550                         img->drop();
1551                         rfile->drop();
1552                 }
1553
1554                 ClientEvent event;
1555                 event.type = CE_TEXTURES_UPDATED;
1556                 m_client_event_queue.push_back(event);
1557         }
1558         else if(command == TOCLIENT_TOOLDEF)
1559         {
1560                 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1561         }
1562         else if(command == TOCLIENT_NODEDEF)
1563         {
1564                 infostream<<"Client: Received node definitions: packet size: "
1565                                 <<datasize<<std::endl;
1566
1567                 // Mesh update thread must be stopped while
1568                 // updating content definitions
1569                 assert(!m_mesh_update_thread.IsRunning());
1570
1571                 // Decompress node definitions
1572                 std::string datastring((char*)&data[2], datasize-2);
1573                 std::istringstream is(datastring, std::ios_base::binary);
1574                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1575                 std::ostringstream tmp_os;
1576                 decompressZlib(tmp_is, tmp_os);
1577
1578                 // Deserialize node definitions
1579                 std::istringstream tmp_is2(tmp_os.str());
1580                 m_nodedef->deSerialize(tmp_is2);
1581                 m_nodedef_received = true;
1582         }
1583         else if(command == TOCLIENT_CRAFTITEMDEF)
1584         {
1585                 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1586         }
1587         else if(command == TOCLIENT_ITEMDEF)
1588         {
1589                 infostream<<"Client: Received item definitions: packet size: "
1590                                 <<datasize<<std::endl;
1591
1592                 // Mesh update thread must be stopped while
1593                 // updating content definitions
1594                 assert(!m_mesh_update_thread.IsRunning());
1595
1596                 // Decompress item definitions
1597                 std::string datastring((char*)&data[2], datasize-2);
1598                 std::istringstream is(datastring, std::ios_base::binary);
1599                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1600                 std::ostringstream tmp_os;
1601                 decompressZlib(tmp_is, tmp_os);
1602
1603                 // Deserialize node definitions
1604                 std::istringstream tmp_is2(tmp_os.str());
1605                 m_itemdef->deSerialize(tmp_is2);
1606                 m_itemdef_received = true;
1607         }
1608         else
1609         {
1610                 infostream<<"Client: Ignoring unknown command "
1611                                 <<command<<std::endl;
1612         }
1613 }
1614
1615 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1616 {
1617         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1618         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1619 }
1620
1621 void Client::interact(u8 action, const PointedThing& pointed)
1622 {
1623         if(connectedAndInitialized() == false){
1624                 infostream<<"Client::interact() "
1625                                 "cancelled (not connected)"
1626                                 <<std::endl;
1627                 return;
1628         }
1629
1630         std::ostringstream os(std::ios_base::binary);
1631
1632         /*
1633                 [0] u16 command
1634                 [2] u8 action
1635                 [3] u16 item
1636                 [5] u32 length of the next item
1637                 [9] serialized PointedThing
1638                 actions:
1639                 0: start digging (from undersurface) or use
1640                 1: stop digging (all parameters ignored)
1641                 2: digging completed
1642                 3: place block or item (to abovesurface)
1643                 4: use item
1644         */
1645         writeU16(os, TOSERVER_INTERACT);
1646         writeU8(os, action);
1647         writeU16(os, getPlayerItem());
1648         std::ostringstream tmp_os(std::ios::binary);
1649         pointed.serialize(tmp_os);
1650         os<<serializeLongString(tmp_os.str());
1651
1652         std::string s = os.str();
1653         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1654
1655         // Send as reliable
1656         Send(0, data, true);
1657 }
1658
1659 void Client::sendSignNodeText(v3s16 p, std::string text)
1660 {
1661         /*
1662                 u16 command
1663                 v3s16 p
1664                 u16 textlen
1665                 textdata
1666         */
1667         std::ostringstream os(std::ios_base::binary);
1668         u8 buf[12];
1669         
1670         // Write command
1671         writeU16(buf, TOSERVER_SIGNNODETEXT);
1672         os.write((char*)buf, 2);
1673         
1674         // Write p
1675         writeV3S16(buf, p);
1676         os.write((char*)buf, 6);
1677
1678         u16 textlen = text.size();
1679         // Write text length
1680         writeS16(buf, textlen);
1681         os.write((char*)buf, 2);
1682
1683         // Write text
1684         os.write((char*)text.c_str(), textlen);
1685         
1686         // Make data buffer
1687         std::string s = os.str();
1688         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1689         // Send as reliable
1690         Send(0, data, true);
1691 }
1692         
1693 void Client::sendInventoryAction(InventoryAction *a)
1694 {
1695         std::ostringstream os(std::ios_base::binary);
1696         u8 buf[12];
1697         
1698         // Write command
1699         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1700         os.write((char*)buf, 2);
1701
1702         a->serialize(os);
1703         
1704         // Make data buffer
1705         std::string s = os.str();
1706         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1707         // Send as reliable
1708         Send(0, data, true);
1709 }
1710
1711 void Client::sendChatMessage(const std::wstring &message)
1712 {
1713         std::ostringstream os(std::ios_base::binary);
1714         u8 buf[12];
1715         
1716         // Write command
1717         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1718         os.write((char*)buf, 2);
1719         
1720         // Write length
1721         writeU16(buf, message.size());
1722         os.write((char*)buf, 2);
1723         
1724         // Write string
1725         for(u32 i=0; i<message.size(); i++)
1726         {
1727                 u16 w = message[i];
1728                 writeU16(buf, w);
1729                 os.write((char*)buf, 2);
1730         }
1731         
1732         // Make data buffer
1733         std::string s = os.str();
1734         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1735         // Send as reliable
1736         Send(0, data, true);
1737 }
1738
1739 void Client::sendChangePassword(const std::wstring oldpassword,
1740                 const std::wstring newpassword)
1741 {
1742         Player *player = m_env.getLocalPlayer();
1743         if(player == NULL)
1744                 return;
1745
1746         std::string playername = player->getName();
1747         std::string oldpwd = translatePassword(playername, oldpassword);
1748         std::string newpwd = translatePassword(playername, newpassword);
1749
1750         std::ostringstream os(std::ios_base::binary);
1751         u8 buf[2+PASSWORD_SIZE*2];
1752         /*
1753                 [0] u16 TOSERVER_PASSWORD
1754                 [2] u8[28] old password
1755                 [30] u8[28] new password
1756         */
1757
1758         writeU16(buf, TOSERVER_PASSWORD);
1759         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1760         {
1761                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1762                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1763         }
1764         buf[2+PASSWORD_SIZE-1] = 0;
1765         buf[30+PASSWORD_SIZE-1] = 0;
1766         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1767
1768         // Make data buffer
1769         std::string s = os.str();
1770         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1771         // Send as reliable
1772         Send(0, data, true);
1773 }
1774
1775
1776 void Client::sendDamage(u8 damage)
1777 {
1778         DSTACK(__FUNCTION_NAME);
1779         std::ostringstream os(std::ios_base::binary);
1780
1781         writeU16(os, TOSERVER_DAMAGE);
1782         writeU8(os, damage);
1783
1784         // Make data buffer
1785         std::string s = os.str();
1786         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1787         // Send as reliable
1788         Send(0, data, true);
1789 }
1790
1791 void Client::sendRespawn()
1792 {
1793         DSTACK(__FUNCTION_NAME);
1794         std::ostringstream os(std::ios_base::binary);
1795
1796         writeU16(os, TOSERVER_RESPAWN);
1797
1798         // Make data buffer
1799         std::string s = os.str();
1800         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1801         // Send as reliable
1802         Send(0, data, true);
1803 }
1804
1805 void Client::sendPlayerPos()
1806 {
1807         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1808         
1809         Player *myplayer = m_env.getLocalPlayer();
1810         if(myplayer == NULL)
1811                 return;
1812         
1813         u16 our_peer_id;
1814         {
1815                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1816                 our_peer_id = m_con.GetPeerID();
1817         }
1818         
1819         // Set peer id if not set already
1820         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1821                 myplayer->peer_id = our_peer_id;
1822         // Check that an existing peer_id is the same as the connection's
1823         assert(myplayer->peer_id == our_peer_id);
1824         
1825         v3f pf = myplayer->getPosition();
1826         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1827         v3f sf = myplayer->getSpeed();
1828         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1829         s32 pitch = myplayer->getPitch() * 100;
1830         s32 yaw = myplayer->getYaw() * 100;
1831
1832         /*
1833                 Format:
1834                 [0] u16 command
1835                 [2] v3s32 position*100
1836                 [2+12] v3s32 speed*100
1837                 [2+12+12] s32 pitch*100
1838                 [2+12+12+4] s32 yaw*100
1839         */
1840
1841         SharedBuffer<u8> data(2+12+12+4+4);
1842         writeU16(&data[0], TOSERVER_PLAYERPOS);
1843         writeV3S32(&data[2], position);
1844         writeV3S32(&data[2+12], speed);
1845         writeS32(&data[2+12+12], pitch);
1846         writeS32(&data[2+12+12+4], yaw);
1847
1848         // Send as unreliable
1849         Send(0, data, false);
1850 }
1851
1852 void Client::sendPlayerItem(u16 item)
1853 {
1854         Player *myplayer = m_env.getLocalPlayer();
1855         if(myplayer == NULL)
1856                 return;
1857
1858         u16 our_peer_id = m_con.GetPeerID();
1859
1860         // Set peer id if not set already
1861         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1862                 myplayer->peer_id = our_peer_id;
1863         // Check that an existing peer_id is the same as the connection's
1864         assert(myplayer->peer_id == our_peer_id);
1865
1866         SharedBuffer<u8> data(2+2);
1867         writeU16(&data[0], TOSERVER_PLAYERITEM);
1868         writeU16(&data[2], item);
1869
1870         // Send as reliable
1871         Send(0, data, true);
1872 }
1873
1874 void Client::removeNode(v3s16 p)
1875 {
1876         core::map<v3s16, MapBlock*> modified_blocks;
1877
1878         try
1879         {
1880                 //TimeTaker t("removeNodeAndUpdate", m_device);
1881                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1882         }
1883         catch(InvalidPositionException &e)
1884         {
1885         }
1886         
1887         // add urgent task to update the modified node
1888         addUpdateMeshTaskForNode(p, false, true);
1889
1890         for(core::map<v3s16, MapBlock * >::Iterator
1891                         i = modified_blocks.getIterator();
1892                         i.atEnd() == false; i++)
1893         {
1894                 v3s16 p = i.getNode()->getKey();
1895                 addUpdateMeshTaskWithEdge(p);
1896         }
1897 }
1898
1899 void Client::addNode(v3s16 p, MapNode n)
1900 {
1901         TimeTaker timer1("Client::addNode()");
1902
1903         core::map<v3s16, MapBlock*> modified_blocks;
1904
1905         try
1906         {
1907                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1908                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1909         }
1910         catch(InvalidPositionException &e)
1911         {}
1912         
1913         for(core::map<v3s16, MapBlock * >::Iterator
1914                         i = modified_blocks.getIterator();
1915                         i.atEnd() == false; i++)
1916         {
1917                 v3s16 p = i.getNode()->getKey();
1918                 addUpdateMeshTaskWithEdge(p);
1919         }
1920 }
1921         
1922 void Client::setPlayerControl(PlayerControl &control)
1923 {
1924         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1925         LocalPlayer *player = m_env.getLocalPlayer();
1926         assert(player != NULL);
1927         player->control = control;
1928 }
1929
1930 void Client::selectPlayerItem(u16 item)
1931 {
1932         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1933         m_playeritem = item;
1934         m_inventory_updated = true;
1935         sendPlayerItem(item);
1936 }
1937
1938 // Returns true if the inventory of the local player has been
1939 // updated from the server. If it is true, it is set to false.
1940 bool Client::getLocalInventoryUpdated()
1941 {
1942         // m_inventory_updated is behind envlock
1943         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1944         bool updated = m_inventory_updated;
1945         m_inventory_updated = false;
1946         return updated;
1947 }
1948
1949 // Copies the inventory of the local player to parameter
1950 void Client::getLocalInventory(Inventory &dst)
1951 {
1952         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1953         Player *player = m_env.getLocalPlayer();
1954         assert(player != NULL);
1955         dst = player->inventory;
1956 }
1957
1958 Inventory* Client::getInventory(const InventoryLocation &loc)
1959 {
1960         switch(loc.type){
1961         case InventoryLocation::UNDEFINED:
1962         {}
1963         break;
1964         case InventoryLocation::CURRENT_PLAYER:
1965         {
1966                 Player *player = m_env.getLocalPlayer();
1967                 assert(player != NULL);
1968                 return &player->inventory;
1969         }
1970         break;
1971         case InventoryLocation::PLAYER:
1972         {
1973                 Player *player = m_env.getPlayer(loc.name.c_str());
1974                 if(!player)
1975                         return NULL;
1976                 return &player->inventory;
1977         }
1978         break;
1979         case InventoryLocation::NODEMETA:
1980         {
1981                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
1982                 if(!meta)
1983                         return NULL;
1984                 return meta->getInventory();
1985         }
1986         break;
1987         default:
1988                 assert(0);
1989         }
1990         return NULL;
1991 }
1992 void Client::inventoryAction(InventoryAction *a)
1993 {
1994         /*
1995                 Send it to the server
1996         */
1997         sendInventoryAction(a);
1998
1999         /*
2000                 Predict some local inventory changes
2001         */
2002         a->clientApply(this, this);
2003 }
2004
2005 ClientActiveObject * Client::getSelectedActiveObject(
2006                 f32 max_d,
2007                 v3f from_pos_f_on_map,
2008                 core::line3d<f32> shootline_on_map
2009         )
2010 {
2011         core::array<DistanceSortedActiveObject> objects;
2012
2013         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2014
2015         //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2016         
2017         // Sort them.
2018         // After this, the closest object is the first in the array.
2019         objects.sort();
2020
2021         for(u32 i=0; i<objects.size(); i++)
2022         {
2023                 ClientActiveObject *obj = objects[i].obj;
2024                 
2025                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2026                 if(selection_box == NULL)
2027                         continue;
2028
2029                 v3f pos = obj->getPosition();
2030
2031                 core::aabbox3d<f32> offsetted_box(
2032                                 selection_box->MinEdge + pos,
2033                                 selection_box->MaxEdge + pos
2034                 );
2035
2036                 if(offsetted_box.intersectsWithLine(shootline_on_map))
2037                 {
2038                         //infostream<<"Returning selected object"<<std::endl;
2039                         return obj;
2040                 }
2041         }
2042
2043         //infostream<<"No object selected; returning NULL."<<std::endl;
2044         return NULL;
2045 }
2046
2047 void Client::printDebugInfo(std::ostream &os)
2048 {
2049         //JMutexAutoLock lock1(m_fetchblock_mutex);
2050         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2051
2052         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2053                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2054                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2055                 <<std::endl;*/
2056 }
2057
2058 core::list<std::wstring> Client::getConnectedPlayerNames()
2059 {
2060         core::list<Player*> players = m_env.getPlayers(true);
2061         core::list<std::wstring> playerNames;
2062         for(core::list<Player*>::Iterator
2063                         i = players.begin();
2064                         i != players.end(); i++)
2065         {
2066                 Player *player = *i;
2067                 playerNames.push_back(narrow_to_wide(player->getName()));
2068         }
2069         return playerNames;
2070 }
2071
2072 float Client::getAnimationTime()
2073 {
2074         return m_animation_time;
2075 }
2076
2077 int Client::getCrackLevel()
2078 {
2079         return m_crack_level;
2080 }
2081
2082 void Client::setCrack(int level, v3s16 pos)
2083 {
2084         int old_crack_level = m_crack_level;
2085         v3s16 old_crack_pos = m_crack_pos;
2086
2087         m_crack_level = level;
2088         m_crack_pos = pos;
2089
2090         if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2091         {
2092                 // remove old crack
2093                 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2094         }
2095         if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2096         {
2097                 // add new crack
2098                 addUpdateMeshTaskForNode(pos, false, true);
2099         }
2100 }
2101
2102 u16 Client::getHP()
2103 {
2104         Player *player = m_env.getLocalPlayer();
2105         assert(player != NULL);
2106         return player->hp;
2107 }
2108
2109 bool Client::getChatMessage(std::wstring &message)
2110 {
2111         if(m_chat_queue.size() == 0)
2112                 return false;
2113         message = m_chat_queue.pop_front();
2114         return true;
2115 }
2116
2117 void Client::typeChatMessage(const std::wstring &message)
2118 {
2119         // Discard empty line
2120         if(message == L"")
2121                 return;
2122
2123         // Send to others
2124         sendChatMessage(message);
2125
2126         // Show locally
2127         if (message[0] == L'/')
2128         {
2129                 m_chat_queue.push_back(
2130                                 (std::wstring)L"issued command: "+message);
2131         }
2132         else
2133         {
2134                 LocalPlayer *player = m_env.getLocalPlayer();
2135                 assert(player != NULL);
2136                 std::wstring name = narrow_to_wide(player->getName());
2137                 m_chat_queue.push_back(
2138                                 (std::wstring)L"<"+name+L"> "+message);
2139         }
2140 }
2141
2142 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2143 {
2144         /*infostream<<"Client::addUpdateMeshTask(): "
2145                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2146                         <<" ack_to_server="<<ack_to_server
2147                         <<" urgent="<<urgent
2148                         <<std::endl;*/
2149
2150         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2151         if(b == NULL)
2152                 return;
2153         
2154         /*
2155                 Create a task to update the mesh of the block
2156         */
2157         
2158         MeshMakeData *data = new MeshMakeData(this);
2159         
2160         {
2161                 //TimeTaker timer("data fill");
2162                 // Release: ~0ms
2163                 // Debug: 1-6ms, avg=2ms
2164                 data->fill(b);
2165                 data->setCrack(m_crack_level, m_crack_pos);
2166                 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2167         }
2168
2169         // Debug wait
2170         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2171         
2172         // Add task to queue
2173         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2174
2175         /*infostream<<"Mesh update input queue size is "
2176                         <<m_mesh_update_thread.m_queue_in.size()
2177                         <<std::endl;*/
2178 }
2179
2180 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2181 {
2182         /*{
2183                 v3s16 p = blockpos;
2184                 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2185                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2186                                 <<std::endl;
2187         }*/
2188
2189         try{
2190                 v3s16 p = blockpos + v3s16(0,0,0);
2191                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2192                 addUpdateMeshTask(p, ack_to_server, urgent);
2193         }
2194         catch(InvalidPositionException &e){}
2195         // Leading edge
2196         try{
2197                 v3s16 p = blockpos + v3s16(-1,0,0);
2198                 addUpdateMeshTask(p, false, urgent);
2199         }
2200         catch(InvalidPositionException &e){}
2201         try{
2202                 v3s16 p = blockpos + v3s16(0,-1,0);
2203                 addUpdateMeshTask(p, false, urgent);
2204         }
2205         catch(InvalidPositionException &e){}
2206         try{
2207                 v3s16 p = blockpos + v3s16(0,0,-1);
2208                 addUpdateMeshTask(p, false, urgent);
2209         }
2210         catch(InvalidPositionException &e){}
2211 }
2212
2213 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2214 {
2215         {
2216                 v3s16 p = nodepos;
2217                 infostream<<"Client::addUpdateMeshTaskForNode(): "
2218                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2219                                 <<std::endl;
2220         }
2221
2222         v3s16 blockpos = getNodeBlockPos(nodepos);
2223         v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2224
2225         try{
2226                 v3s16 p = blockpos + v3s16(0,0,0);
2227                 addUpdateMeshTask(p, ack_to_server, urgent);
2228         }
2229         catch(InvalidPositionException &e){}
2230         // Leading edge
2231         if(nodepos.X == blockpos_relative.X){
2232                 try{
2233                         v3s16 p = blockpos + v3s16(-1,0,0);
2234                         addUpdateMeshTask(p, false, urgent);
2235                 }
2236                 catch(InvalidPositionException &e){}
2237         }
2238         if(nodepos.Y == blockpos_relative.Y){
2239                 try{
2240                         v3s16 p = blockpos + v3s16(0,-1,0);
2241                         addUpdateMeshTask(p, false, urgent);
2242                 }
2243                 catch(InvalidPositionException &e){}
2244         }
2245         if(nodepos.Z == blockpos_relative.Z){
2246                 try{
2247                         v3s16 p = blockpos + v3s16(0,0,-1);
2248                         addUpdateMeshTask(p, false, urgent);
2249                 }
2250                 catch(InvalidPositionException &e){}
2251         }
2252 }
2253
2254 ClientEvent Client::getClientEvent()
2255 {
2256         if(m_client_event_queue.size() == 0)
2257         {
2258                 ClientEvent event;
2259                 event.type = CE_NONE;
2260                 return event;
2261         }
2262         return m_client_event_queue.pop_front();
2263 }
2264
2265 void Client::afterContentReceived()
2266 {
2267         assert(m_itemdef_received);
2268         assert(m_nodedef_received);
2269         assert(m_textures_received);
2270
2271         // Rebuild inherited images and recreate textures
2272         m_tsrc->rebuildImagesAndTextures();
2273
2274         // Update texture atlas
2275         if(g_settings->getBool("enable_texture_atlas"))
2276                 m_tsrc->buildMainAtlas(this);
2277
2278         // Update node aliases
2279         m_nodedef->updateAliases(m_itemdef);
2280
2281         // Update node textures
2282         m_nodedef->updateTextures(m_tsrc);
2283
2284         // Update item textures and meshes
2285         m_itemdef->updateTexturesAndMeshes(this);
2286
2287         // Start mesh update thread after setting up content definitions
2288         m_mesh_update_thread.Start();
2289 }
2290
2291 float Client::getRTT(void)
2292 {
2293         try{
2294                 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2295         } catch(con::PeerNotFoundException &e){
2296                 return 1337;
2297         }
2298 }
2299
2300 // IGameDef interface
2301 // Under envlock
2302 IItemDefManager* Client::getItemDefManager()
2303 {
2304         return m_itemdef;
2305 }
2306 INodeDefManager* Client::getNodeDefManager()
2307 {
2308         return m_nodedef;
2309 }
2310 ICraftDefManager* Client::getCraftDefManager()
2311 {
2312         return NULL;
2313         //return m_craftdef;
2314 }
2315 ITextureSource* Client::getTextureSource()
2316 {
2317         return m_tsrc;
2318 }
2319 u16 Client::allocateUnknownNodeId(const std::string &name)
2320 {
2321         errorstream<<"Client::allocateUnknownNodeId(): "
2322                         <<"Client cannot allocate node IDs"<<std::endl;
2323         assert(0);
2324         return CONTENT_IGNORE;
2325 }
2326