ToolDefManager serialization
[oweals/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], 3);
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
1510         {
1511                 infostream<<"Client: Ignoring unknown command "
1512                                 <<command<<std::endl;
1513         }
1514 }
1515
1516 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1517 {
1518         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1519         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1520 }
1521
1522 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1523                 v3s16 nodepos_oversurface, u16 item)
1524 {
1525         if(connectedAndInitialized() == false){
1526                 infostream<<"Client::groundAction() "
1527                                 "cancelled (not connected)"
1528                                 <<std::endl;
1529                 return;
1530         }
1531         
1532         /*
1533                 length: 17
1534                 [0] u16 command
1535                 [2] u8 action
1536                 [3] v3s16 nodepos_undersurface
1537                 [9] v3s16 nodepos_abovesurface
1538                 [15] u16 item
1539                 actions:
1540                 0: start digging
1541                 1: place block
1542                 2: stop digging (all parameters ignored)
1543                 3: digging completed
1544         */
1545         u8 datasize = 2 + 1 + 6 + 6 + 2;
1546         SharedBuffer<u8> data(datasize);
1547         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1548         writeU8(&data[2], action);
1549         writeV3S16(&data[3], nodepos_undersurface);
1550         writeV3S16(&data[9], nodepos_oversurface);
1551         writeU16(&data[15], item);
1552         Send(0, data, true);
1553 }
1554
1555 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1556 {
1557         if(connectedAndInitialized() == false){
1558                 infostream<<"Client::clickActiveObject() "
1559                                 "cancelled (not connected)"
1560                                 <<std::endl;
1561                 return;
1562         }
1563
1564         Player *player = m_env.getLocalPlayer();
1565         if(player == NULL)
1566                 return;
1567
1568         ClientActiveObject *obj = m_env.getActiveObject(id);
1569         if(obj){
1570                 if(button == 0){
1571                         ToolItem *titem = NULL;
1572                         std::string toolname = "";
1573
1574                         InventoryList *mlist = player->inventory.getList("main");
1575                         if(mlist != NULL)
1576                         {
1577                                 InventoryItem *item = mlist->getItem(item_i);
1578                                 if(item && (std::string)item->getName() == "ToolItem")
1579                                 {
1580                                         titem = (ToolItem*)item;
1581                                         toolname = titem->getToolName();
1582                                 }
1583                         }
1584
1585                         v3f playerpos = player->getPosition();
1586                         v3f objpos = obj->getPosition();
1587                         v3f dir = (objpos - playerpos).normalize();
1588                         
1589                         bool disable_send = obj->directReportPunch(toolname, dir);
1590                         
1591                         if(disable_send)
1592                                 return;
1593                 }
1594         }
1595         
1596         /*
1597                 length: 7
1598                 [0] u16 command
1599                 [2] u8 button (0=left, 1=right)
1600                 [3] u16 id
1601                 [5] u16 item
1602         */
1603         u8 datasize = 2 + 1 + 6 + 2 + 2;
1604         SharedBuffer<u8> data(datasize);
1605         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1606         writeU8(&data[2], button);
1607         writeU16(&data[3], id);
1608         writeU16(&data[5], item_i);
1609         Send(0, data, true);
1610 }
1611
1612 void Client::sendSignNodeText(v3s16 p, std::string text)
1613 {
1614         /*
1615                 u16 command
1616                 v3s16 p
1617                 u16 textlen
1618                 textdata
1619         */
1620         std::ostringstream os(std::ios_base::binary);
1621         u8 buf[12];
1622         
1623         // Write command
1624         writeU16(buf, TOSERVER_SIGNNODETEXT);
1625         os.write((char*)buf, 2);
1626         
1627         // Write p
1628         writeV3S16(buf, p);
1629         os.write((char*)buf, 6);
1630
1631         u16 textlen = text.size();
1632         // Write text length
1633         writeS16(buf, textlen);
1634         os.write((char*)buf, 2);
1635
1636         // Write text
1637         os.write((char*)text.c_str(), textlen);
1638         
1639         // Make data buffer
1640         std::string s = os.str();
1641         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1642         // Send as reliable
1643         Send(0, data, true);
1644 }
1645         
1646 void Client::sendInventoryAction(InventoryAction *a)
1647 {
1648         std::ostringstream os(std::ios_base::binary);
1649         u8 buf[12];
1650         
1651         // Write command
1652         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1653         os.write((char*)buf, 2);
1654
1655         a->serialize(os);
1656         
1657         // Make data buffer
1658         std::string s = os.str();
1659         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1660         // Send as reliable
1661         Send(0, data, true);
1662 }
1663
1664 void Client::sendChatMessage(const std::wstring &message)
1665 {
1666         std::ostringstream os(std::ios_base::binary);
1667         u8 buf[12];
1668         
1669         // Write command
1670         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1671         os.write((char*)buf, 2);
1672         
1673         // Write length
1674         writeU16(buf, message.size());
1675         os.write((char*)buf, 2);
1676         
1677         // Write string
1678         for(u32 i=0; i<message.size(); i++)
1679         {
1680                 u16 w = message[i];
1681                 writeU16(buf, w);
1682                 os.write((char*)buf, 2);
1683         }
1684         
1685         // Make data buffer
1686         std::string s = os.str();
1687         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1688         // Send as reliable
1689         Send(0, data, true);
1690 }
1691
1692 void Client::sendChangePassword(const std::wstring oldpassword,
1693                 const std::wstring newpassword)
1694 {
1695         Player *player = m_env.getLocalPlayer();
1696         if(player == NULL)
1697                 return;
1698
1699         std::string playername = player->getName();
1700         std::string oldpwd = translatePassword(playername, oldpassword);
1701         std::string newpwd = translatePassword(playername, newpassword);
1702
1703         std::ostringstream os(std::ios_base::binary);
1704         u8 buf[2+PASSWORD_SIZE*2];
1705         /*
1706                 [0] u16 TOSERVER_PASSWORD
1707                 [2] u8[28] old password
1708                 [30] u8[28] new password
1709         */
1710
1711         writeU16(buf, TOSERVER_PASSWORD);
1712         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1713         {
1714                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1715                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1716         }
1717         buf[2+PASSWORD_SIZE-1] = 0;
1718         buf[30+PASSWORD_SIZE-1] = 0;
1719         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1720
1721         // Make data buffer
1722         std::string s = os.str();
1723         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1724         // Send as reliable
1725         Send(0, data, true);
1726 }
1727
1728
1729 void Client::sendDamage(u8 damage)
1730 {
1731         DSTACK(__FUNCTION_NAME);
1732         std::ostringstream os(std::ios_base::binary);
1733
1734         writeU16(os, TOSERVER_DAMAGE);
1735         writeU8(os, damage);
1736
1737         // Make data buffer
1738         std::string s = os.str();
1739         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1740         // Send as reliable
1741         Send(0, data, true);
1742 }
1743
1744 void Client::sendRespawn()
1745 {
1746         DSTACK(__FUNCTION_NAME);
1747         std::ostringstream os(std::ios_base::binary);
1748
1749         writeU16(os, TOSERVER_RESPAWN);
1750
1751         // Make data buffer
1752         std::string s = os.str();
1753         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1754         // Send as reliable
1755         Send(0, data, true);
1756 }
1757
1758 void Client::sendPlayerPos()
1759 {
1760         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1761         
1762         Player *myplayer = m_env.getLocalPlayer();
1763         if(myplayer == NULL)
1764                 return;
1765         
1766         u16 our_peer_id;
1767         {
1768                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1769                 our_peer_id = m_con.GetPeerID();
1770         }
1771         
1772         // Set peer id if not set already
1773         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1774                 myplayer->peer_id = our_peer_id;
1775         // Check that an existing peer_id is the same as the connection's
1776         assert(myplayer->peer_id == our_peer_id);
1777         
1778         v3f pf = myplayer->getPosition();
1779         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1780         v3f sf = myplayer->getSpeed();
1781         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1782         s32 pitch = myplayer->getPitch() * 100;
1783         s32 yaw = myplayer->getYaw() * 100;
1784
1785         /*
1786                 Format:
1787                 [0] u16 command
1788                 [2] v3s32 position*100
1789                 [2+12] v3s32 speed*100
1790                 [2+12+12] s32 pitch*100
1791                 [2+12+12+4] s32 yaw*100
1792         */
1793
1794         SharedBuffer<u8> data(2+12+12+4+4);
1795         writeU16(&data[0], TOSERVER_PLAYERPOS);
1796         writeV3S32(&data[2], position);
1797         writeV3S32(&data[2+12], speed);
1798         writeS32(&data[2+12+12], pitch);
1799         writeS32(&data[2+12+12+4], yaw);
1800
1801         // Send as unreliable
1802         Send(0, data, false);
1803 }
1804
1805 void Client::sendPlayerItem(u16 item)
1806 {
1807         Player *myplayer = m_env.getLocalPlayer();
1808         if(myplayer == NULL)
1809                 return;
1810
1811         u16 our_peer_id = m_con.GetPeerID();
1812
1813         // Set peer id if not set already
1814         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1815                 myplayer->peer_id = our_peer_id;
1816         // Check that an existing peer_id is the same as the connection's
1817         assert(myplayer->peer_id == our_peer_id);
1818
1819         SharedBuffer<u8> data(2+2);
1820         writeU16(&data[0], TOSERVER_PLAYERITEM);
1821         writeU16(&data[2], item);
1822
1823         // Send as reliable
1824         Send(0, data, true);
1825 }
1826
1827 void Client::removeNode(v3s16 p)
1828 {
1829         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1830         
1831         core::map<v3s16, MapBlock*> modified_blocks;
1832
1833         try
1834         {
1835                 //TimeTaker t("removeNodeAndUpdate", m_device);
1836                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1837         }
1838         catch(InvalidPositionException &e)
1839         {
1840         }
1841         
1842         for(core::map<v3s16, MapBlock * >::Iterator
1843                         i = modified_blocks.getIterator();
1844                         i.atEnd() == false; i++)
1845         {
1846                 v3s16 p = i.getNode()->getKey();
1847                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1848                 addUpdateMeshTaskWithEdge(p);
1849         }
1850 }
1851
1852 void Client::addNode(v3s16 p, MapNode n)
1853 {
1854         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1855
1856         TimeTaker timer1("Client::addNode()");
1857
1858         core::map<v3s16, MapBlock*> modified_blocks;
1859
1860         try
1861         {
1862                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1863                 std::string st = std::string("");
1864                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1865         }
1866         catch(InvalidPositionException &e)
1867         {}
1868         
1869         //TimeTaker timer2("Client::addNode(): updateMeshes");
1870
1871         for(core::map<v3s16, MapBlock * >::Iterator
1872                         i = modified_blocks.getIterator();
1873                         i.atEnd() == false; i++)
1874         {
1875                 v3s16 p = i.getNode()->getKey();
1876                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1877                 addUpdateMeshTaskWithEdge(p);
1878         }
1879 }
1880         
1881 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1882 {
1883         m_env.getClientMap().updateCamera(pos, dir, fov);
1884 }
1885
1886 void Client::renderPostFx()
1887 {
1888         m_env.getClientMap().renderPostFx();
1889 }
1890
1891 MapNode Client::getNode(v3s16 p)
1892 {
1893         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1894         return m_env.getMap().getNode(p);
1895 }
1896
1897 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1898 {
1899         return m_env.getMap().getNodeMetadata(p);
1900 }
1901
1902 LocalPlayer* Client::getLocalPlayer()
1903 {
1904         return m_env.getLocalPlayer();
1905 }
1906
1907 void Client::setPlayerControl(PlayerControl &control)
1908 {
1909         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1910         LocalPlayer *player = m_env.getLocalPlayer();
1911         assert(player != NULL);
1912         player->control = control;
1913 }
1914
1915 void Client::selectPlayerItem(u16 item)
1916 {
1917         LocalPlayer *player = m_env.getLocalPlayer();
1918         assert(player != NULL);
1919
1920         player->wieldItem(item);
1921
1922         sendPlayerItem(item);
1923 }
1924
1925 // Returns true if the inventory of the local player has been
1926 // updated from the server. If it is true, it is set to false.
1927 bool Client::getLocalInventoryUpdated()
1928 {
1929         // m_inventory_updated is behind envlock
1930         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1931         bool updated = m_inventory_updated;
1932         m_inventory_updated = false;
1933         return updated;
1934 }
1935
1936 // Copies the inventory of the local player to parameter
1937 void Client::getLocalInventory(Inventory &dst)
1938 {
1939         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1940         Player *player = m_env.getLocalPlayer();
1941         assert(player != NULL);
1942         dst = player->inventory;
1943 }
1944
1945 InventoryContext *Client::getInventoryContext()
1946 {
1947         return &m_inventory_context;
1948 }
1949
1950 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1951 {
1952         if(id == "current_player")
1953         {
1954                 assert(c->current_player);
1955                 return &(c->current_player->inventory);
1956         }
1957         
1958         Strfnd fn(id);
1959         std::string id0 = fn.next(":");
1960
1961         if(id0 == "nodemeta")
1962         {
1963                 v3s16 p;
1964                 p.X = stoi(fn.next(","));
1965                 p.Y = stoi(fn.next(","));
1966                 p.Z = stoi(fn.next(","));
1967                 NodeMetadata* meta = getNodeMetadata(p);
1968                 if(meta)
1969                         return meta->getInventory();
1970                 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1971                                 <<"no metadata found"<<std::endl;
1972                 return NULL;
1973         }
1974
1975         infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1976         return NULL;
1977 }
1978 void Client::inventoryAction(InventoryAction *a)
1979 {
1980         sendInventoryAction(a);
1981 }
1982
1983 ClientActiveObject * Client::getSelectedActiveObject(
1984                 f32 max_d,
1985                 v3f from_pos_f_on_map,
1986                 core::line3d<f32> shootline_on_map
1987         )
1988 {
1989         core::array<DistanceSortedActiveObject> objects;
1990
1991         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1992
1993         //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1994         
1995         // Sort them.
1996         // After this, the closest object is the first in the array.
1997         objects.sort();
1998
1999         for(u32 i=0; i<objects.size(); i++)
2000         {
2001                 ClientActiveObject *obj = objects[i].obj;
2002                 
2003                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2004                 if(selection_box == NULL)
2005                         continue;
2006
2007                 v3f pos = obj->getPosition();
2008
2009                 core::aabbox3d<f32> offsetted_box(
2010                                 selection_box->MinEdge + pos,
2011                                 selection_box->MaxEdge + pos
2012                 );
2013
2014                 if(offsetted_box.intersectsWithLine(shootline_on_map))
2015                 {
2016                         //infostream<<"Returning selected object"<<std::endl;
2017                         return obj;
2018                 }
2019         }
2020
2021         //infostream<<"No object selected; returning NULL."<<std::endl;
2022         return NULL;
2023 }
2024
2025 void Client::printDebugInfo(std::ostream &os)
2026 {
2027         //JMutexAutoLock lock1(m_fetchblock_mutex);
2028         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2029
2030         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2031                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2032                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2033                 <<std::endl;*/
2034 }
2035         
2036 u32 Client::getDayNightRatio()
2037 {
2038         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2039         return m_env.getDayNightRatio();
2040 }
2041
2042 u16 Client::getHP()
2043 {
2044         Player *player = m_env.getLocalPlayer();
2045         assert(player != NULL);
2046         return player->hp;
2047 }
2048
2049 void Client::setTempMod(v3s16 p, NodeMod mod)
2050 {
2051         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2052         assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2053
2054         core::map<v3s16, MapBlock*> affected_blocks;
2055         ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2056                         &affected_blocks);
2057
2058         for(core::map<v3s16, MapBlock*>::Iterator
2059                         i = affected_blocks.getIterator();
2060                         i.atEnd() == false; i++)
2061         {
2062                 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2063         }
2064 }
2065
2066 void Client::clearTempMod(v3s16 p)
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()).clearTempMod(p,
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::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2084 {
2085         /*infostream<<"Client::addUpdateMeshTask(): "
2086                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2087                         <<std::endl;*/
2088
2089         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2090         if(b == NULL)
2091                 return;
2092         
2093         /*
2094                 Create a task to update the mesh of the block
2095         */
2096         
2097         MeshMakeData *data = new MeshMakeData;
2098         
2099         {
2100                 //TimeTaker timer("data fill");
2101                 // Release: ~0ms
2102                 // Debug: 1-6ms, avg=2ms
2103                 data->fill(getDayNightRatio(), b);
2104         }
2105
2106         // Debug wait
2107         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2108         
2109         // Add task to queue
2110         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2111
2112         /*infostream<<"Mesh update input queue size is "
2113                         <<m_mesh_update_thread.m_queue_in.size()
2114                         <<std::endl;*/
2115         
2116 #if 0
2117         // Temporary test: make mesh directly in here
2118         {
2119                 //TimeTaker timer("make mesh");
2120                 // 10ms
2121                 scene::SMesh *mesh_new = NULL;
2122                 mesh_new = makeMapBlockMesh(data);
2123                 b->replaceMesh(mesh_new);
2124                 delete data;
2125         }
2126 #endif
2127
2128         /*
2129                 Mark mesh as non-expired at this point so that it can already
2130                 be marked as expired again if the data changes
2131         */
2132         b->setMeshExpired(false);
2133 }
2134
2135 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2136 {
2137         /*{
2138                 v3s16 p = blockpos;
2139                 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2140                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2141                                 <<std::endl;
2142         }*/
2143
2144         try{
2145                 v3s16 p = blockpos + v3s16(0,0,0);
2146                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2147                 addUpdateMeshTask(p, ack_to_server);
2148         }
2149         catch(InvalidPositionException &e){}
2150         // Leading edge
2151         try{
2152                 v3s16 p = blockpos + v3s16(-1,0,0);
2153                 addUpdateMeshTask(p);
2154         }
2155         catch(InvalidPositionException &e){}
2156         try{
2157                 v3s16 p = blockpos + v3s16(0,-1,0);
2158                 addUpdateMeshTask(p);
2159         }
2160         catch(InvalidPositionException &e){}
2161         try{
2162                 v3s16 p = blockpos + v3s16(0,0,-1);
2163                 addUpdateMeshTask(p);
2164         }
2165         catch(InvalidPositionException &e){}
2166 }
2167
2168 ClientEvent Client::getClientEvent()
2169 {
2170         if(m_client_event_queue.size() == 0)
2171         {
2172                 ClientEvent event;
2173                 event.type = CE_NONE;
2174                 return event;
2175         }
2176         return m_client_event_queue.pop_front();
2177 }
2178
2179 float Client::getRTT(void)
2180 {
2181         try{
2182                 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2183         } catch(con::PeerNotFoundException &e){
2184                 return 1337;
2185         }
2186 }
2187
2188 // IGameDef interface
2189 // Under envlock
2190 IToolDefManager* Client::getToolDefManager()
2191 {
2192         return m_tooldef;
2193 }
2194 INodeDefManager* Client::getNodeDefManager()
2195 {
2196         return m_nodedef;
2197 }
2198 ITextureSource* Client::getTextureSource()
2199 {
2200         return m_tsrc;
2201 }
2202