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