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