working nicely
[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
28 #ifdef _WIN32
29         #include <windows.h>
30         #define sleep_ms(x) Sleep(x)
31 #else
32         #include <unistd.h>
33         #define sleep_ms(x) usleep(x*1000)
34 #endif
35
36 void * ClientUpdateThread::Thread()
37 {
38         ThreadStarted();
39
40         DSTACK(__FUNCTION_NAME);
41
42 #if CATCH_UNHANDLED_EXCEPTIONS
43         try
44         {
45 #endif
46                 while(getRun())
47                 {
48                         m_client->asyncStep();
49
50                         bool was = m_client->AsyncProcessData();
51
52                         if(was == false)
53                                 sleep_ms(10);
54                 }
55 #if CATCH_UNHANDLED_EXCEPTIONS
56         }
57         /*
58                 This is what has to be done in threads to get suitable debug info
59         */
60         catch(std::exception &e)
61         {
62                 dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
63                                 <<e.what()<<std::endl;
64                 assert(0);
65         }
66 #endif
67
68         return NULL;
69 }
70
71 Client::Client(IrrlichtDevice *device, video::SMaterial *materials,
72                 float delete_unused_sectors_timeout,
73                 const char *playername):
74         m_thread(this),
75         m_env(new ClientMap(this, materials,
76                         device->getSceneManager()->getRootSceneNode(),
77                         device->getSceneManager(), 666),
78                         dout_client),
79         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
80         m_device(device),
81         camera_position(0,0,0),
82         camera_direction(0,0,1),
83         m_server_ser_ver(SER_FMT_VER_INVALID),
84         m_step_dtime(0.0),
85         m_delete_unused_sectors_timeout(delete_unused_sectors_timeout),
86         m_inventory_updated(false)
87 {
88         //m_fetchblock_mutex.Init();
89         m_incoming_queue_mutex.Init();
90         m_env_mutex.Init();
91         m_con_mutex.Init();
92         m_step_dtime_mutex.Init();
93
94         m_thread.Start();
95         
96         {
97                 JMutexAutoLock envlock(m_env_mutex);
98                 //m_env.getMap().StartUpdater();
99
100                 Player *player = new LocalPlayer();
101
102                 player->updateName(playername);
103
104                 /*f32 y = BS*2 + BS*20;
105                 player->setPosition(v3f(0, y, 0));*/
106                 //player->setPosition(v3f(0, y, 30900*BS)); // DEBUG
107                 m_env.addPlayer(player);
108         }
109 }
110
111 Client::~Client()
112 {
113         m_thread.setRun(false);
114         while(m_thread.IsRunning())
115                 sleep_ms(100);
116 }
117
118 void Client::connect(Address address)
119 {
120         DSTACK(__FUNCTION_NAME);
121         JMutexAutoLock lock(m_con_mutex);
122         m_con.setTimeoutMs(0);
123         m_con.Connect(address);
124 }
125
126 bool Client::connectedAndInitialized()
127 {
128         JMutexAutoLock lock(m_con_mutex);
129
130         if(m_con.Connected() == false)
131                 return false;
132         
133         if(m_server_ser_ver == SER_FMT_VER_INVALID)
134                 return false;
135         
136         return true;
137 }
138
139 void Client::step(float dtime)
140 {
141         DSTACK(__FUNCTION_NAME);
142         
143         // Limit a bit
144         if(dtime > 2.0)
145                 dtime = 2.0;
146         
147         //dstream<<"Client steps "<<dtime<<std::endl;
148
149         {
150                 //TimeTaker timer("ReceiveAll()", m_device);
151                 // 0ms
152                 ReceiveAll();
153         }
154         
155         {
156                 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
157                 // 0ms
158                 JMutexAutoLock lock(m_con_mutex);
159                 m_con.RunTimeouts(dtime);
160         }
161
162         /*
163                 Packet counter
164         */
165         {
166                 static float counter = -0.001;
167                 counter -= dtime;
168                 if(counter <= 0.0)
169                 {
170                         counter = 20.0;
171                         
172                         dout_client<<"Client packetcounter (20s):"<<std::endl;
173                         m_packetcounter.print(dout_client);
174                         m_packetcounter.clear();
175                 }
176         }
177
178         {
179                 /*
180                         Delete unused sectors
181
182                         NOTE: This jams the game for a while because deleting sectors
183                               clear caches
184                 */
185                 
186                 static float counter = -0.001;
187                 counter -= dtime;
188                 if(counter <= 0.0)
189                 {
190                         // 3 minute interval
191                         counter = 180.0;
192
193                         JMutexAutoLock lock(m_env_mutex);
194
195                         core::list<v3s16> deleted_blocks;
196         
197                         // Delete sector blocks
198                         /*u32 num = m_env.getMap().deleteUnusedSectors
199                                         (m_delete_unused_sectors_timeout,
200                                         true, &deleted_blocks);*/
201                         
202                         // Delete whole sectors
203                         u32 num = m_env.getMap().deleteUnusedSectors
204                                         (m_delete_unused_sectors_timeout,
205                                         false, &deleted_blocks);
206
207                         if(num > 0)
208                         {
209                                 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
210                                                 <<" unused sectors"<<std::endl;*/
211                                 dstream<<DTIME<<"Client: Deleted "<<num
212                                                 <<" unused sectors"<<std::endl;
213                                 
214                                 /*
215                                         Send info to server
216                                 */
217
218                                 // Env is locked so con can be locked.
219                                 JMutexAutoLock lock(m_con_mutex);
220                                 
221                                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
222                                 core::list<v3s16> sendlist;
223                                 for(;;)
224                                 {
225                                         if(sendlist.size() == 255 || i == deleted_blocks.end())
226                                         {
227                                                 if(sendlist.size() == 0)
228                                                         break;
229                                                 /*
230                                                         [0] u16 command
231                                                         [2] u8 count
232                                                         [3] v3s16 pos_0
233                                                         [3+6] v3s16 pos_1
234                                                         ...
235                                                 */
236                                                 u32 replysize = 2+1+6*sendlist.size();
237                                                 SharedBuffer<u8> reply(replysize);
238                                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
239                                                 reply[2] = sendlist.size();
240                                                 u32 k = 0;
241                                                 for(core::list<v3s16>::Iterator
242                                                                 j = sendlist.begin();
243                                                                 j != sendlist.end(); j++)
244                                                 {
245                                                         writeV3S16(&reply[2+1+6*k], *j);
246                                                         k++;
247                                                 }
248                                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
249
250                                                 if(i == deleted_blocks.end())
251                                                         break;
252
253                                                 sendlist.clear();
254                                         }
255
256                                         sendlist.push_back(*i);
257                                         i++;
258                                 }
259                         }
260                 }
261         }
262
263         bool connected = connectedAndInitialized();
264
265         if(connected == false)
266         {
267                 static float counter = -0.001;
268                 counter -= dtime;
269                 if(counter <= 0.0)
270                 {
271                         counter = 2.0;
272
273                         JMutexAutoLock envlock(m_env_mutex);
274                         
275                         Player *myplayer = m_env.getLocalPlayer();
276                         assert(myplayer != NULL);
277         
278                         // Send TOSERVER_INIT
279                         // [0] u16 TOSERVER_INIT
280                         // [2] u8 SER_FMT_VER_HIGHEST
281                         // [3] u8[20] player_name
282                         SharedBuffer<u8> data(2+1+20);
283                         writeU16(&data[0], TOSERVER_INIT);
284                         writeU8(&data[2], SER_FMT_VER_HIGHEST);
285                         memcpy(&data[3], myplayer->getName(), 20);
286                         // Send as unreliable
287                         Send(0, data, false);
288                 }
289
290                 // Not connected, return
291                 return;
292         }
293
294         /*
295                 Do stuff if connected
296         */
297         
298         {
299                 // 0ms
300                 JMutexAutoLock lock(m_env_mutex);
301
302                 // Control local player (0ms)
303                 LocalPlayer *player = m_env.getLocalPlayer();
304                 assert(player != NULL);
305                 player->applyControl(dtime);
306
307                 //TimeTaker envtimer("env step", m_device);
308                 // Step environment
309                 m_env.step(dtime);
310
311                 // Step active blocks
312                 for(core::map<v3s16, bool>::Iterator
313                                 i = m_active_blocks.getIterator();
314                                 i.atEnd() == false; i++)
315                 {
316                         v3s16 p = i.getNode()->getKey();
317
318                         MapBlock *block = NULL;
319                         try
320                         {
321                                 block = m_env.getMap().getBlockNoCreate(p);
322                                 block->stepObjects(dtime, false);
323                         }
324                         catch(InvalidPositionException &e)
325                         {
326                         }
327                 }
328         }
329
330         {
331                 // Fetch some nearby blocks
332                 //fetchBlocks();
333         }
334
335         {
336                 static float counter = 0.0;
337                 counter += dtime;
338                 if(counter >= 10)
339                 {
340                         counter = 0.0;
341                         JMutexAutoLock lock(m_con_mutex);
342                         // connectedAndInitialized() is true, peer exists.
343                         con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
344                         dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
345                 }
346         }
347         {
348                 // Update at reasonable intervals (0.2s)
349                 static float counter = 0.0;
350                 counter += dtime;
351                 if(counter >= 0.2)
352                 {
353                         counter = 0.0;
354                         sendPlayerPos();
355                 }
356         }
357
358 #if 0
359         /*
360                 Clear old entries from fetchblock history
361         */
362         {
363                 JMutexAutoLock lock(m_fetchblock_mutex);
364                 
365                 core::list<v3s16> remove_queue;
366                 core::map<v3s16, float>::Iterator i;
367                 i = m_fetchblock_history.getIterator();
368                 for(; i.atEnd() == false; i++)
369                 {
370                         float value = i.getNode()->getValue();
371                         value += dtime;
372                         i.getNode()->setValue(value);
373                         if(value >= 60.0)
374                                 remove_queue.push_back(i.getNode()->getKey());
375                 }
376                 core::list<v3s16>::Iterator j;
377                 j = remove_queue.begin();
378                 for(; j != remove_queue.end(); j++)
379                 {
380                         m_fetchblock_history.remove(*j);
381                 }
382         }
383 #endif
384
385         /*{
386                 JMutexAutoLock lock(m_step_dtime_mutex);
387                 m_step_dtime += dtime;
388         }*/
389         
390         /*
391                 BEGIN TEST CODE
392         */
393
394         /*
395                 END OF TEST CODE
396         */
397 }
398
399 float Client::asyncStep()
400 {
401         DSTACK(__FUNCTION_NAME);
402         //dstream<<"Client::asyncStep()"<<std::endl;
403         
404         /*float dtime;
405         {
406                 JMutexAutoLock lock1(m_step_dtime_mutex);
407                 if(m_step_dtime < 0.001)
408                         return 0.0;
409                 dtime = m_step_dtime;
410                 m_step_dtime = 0.0;
411         }
412
413         return dtime;*/
414         return 0.0;
415 }
416
417 // Virtual methods from con::PeerHandler
418 void Client::peerAdded(con::Peer *peer)
419 {
420         derr_client<<"Client::peerAdded(): peer->id="
421                         <<peer->id<<std::endl;
422 }
423 void Client::deletingPeer(con::Peer *peer, bool timeout)
424 {
425         derr_client<<"Client::deletingPeer(): "
426                         "Server Peer is getting deleted "
427                         <<"(timeout="<<timeout<<")"<<std::endl;
428 }
429
430 void Client::ReceiveAll()
431 {
432         DSTACK(__FUNCTION_NAME);
433         for(;;)
434         {
435                 try{
436                         Receive();
437                 }
438                 catch(con::NoIncomingDataException &e)
439                 {
440                         break;
441                 }
442                 catch(con::InvalidIncomingDataException &e)
443                 {
444                         dout_client<<DTIME<<"Client::ReceiveAll(): "
445                                         "InvalidIncomingDataException: what()="
446                                         <<e.what()<<std::endl;
447                 }
448                 //TODO: Testing
449                 //break;
450         }
451 }
452
453 void Client::Receive()
454 {
455         DSTACK(__FUNCTION_NAME);
456         u32 data_maxsize = 10000;
457         Buffer<u8> data(data_maxsize);
458         u16 sender_peer_id;
459         u32 datasize;
460         {
461                 //TimeTaker t1("con mutex and receive", m_device);
462                 JMutexAutoLock lock(m_con_mutex);
463                 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
464         }
465         //TimeTaker t1("ProcessData", m_device);
466         ProcessData(*data, datasize, sender_peer_id);
467 }
468
469 /*
470         sender_peer_id given to this shall be quaranteed to be a valid peer
471 */
472 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
473 {
474         DSTACK(__FUNCTION_NAME);
475
476         // Ignore packets that don't even fit a command
477         if(datasize < 2)
478         {
479                 m_packetcounter.add(60000);
480                 return;
481         }
482
483         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
484
485         //dstream<<"Client: received command="<<command<<std::endl;
486         m_packetcounter.add((u16)command);
487         
488         /*
489                 If this check is removed, be sure to change the queue
490                 system to know the ids
491         */
492         if(sender_peer_id != PEER_ID_SERVER)
493         {
494                 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
495                                 "coming from server: peer_id="<<sender_peer_id
496                                 <<std::endl;
497                 return;
498         }
499
500         con::Peer *peer;
501         {
502                 JMutexAutoLock lock(m_con_mutex);
503                 // All data is coming from the server
504                 // PeerNotFoundException is handled by caller.
505                 peer = m_con.GetPeer(PEER_ID_SERVER);
506         }
507
508         u8 ser_version = m_server_ser_ver;
509
510         //dstream<<"Client received command="<<(int)command<<std::endl;
511
512         // Execute fast commands straight away
513
514         if(command == TOCLIENT_INIT)
515         {
516                 if(datasize < 3)
517                         return;
518
519                 u8 deployed = data[2];
520
521                 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
522                                 "deployed="<<((int)deployed&0xff)<<std::endl;
523
524                 if(deployed < SER_FMT_VER_LOWEST
525                                 || deployed > SER_FMT_VER_HIGHEST)
526                 {
527                         derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
528                                         <<"unsupported ser_fmt_ver"<<std::endl;
529                         return;
530                 }
531                 
532                 m_server_ser_ver = deployed;
533
534                 // Get player position
535                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
536                 if(datasize >= 2+1+6)
537                         playerpos_s16 = readV3S16(&data[2+1]);
538                 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
539
540                 { //envlock
541                         JMutexAutoLock envlock(m_env_mutex);
542                         
543                         // Set player position
544                         Player *player = m_env.getLocalPlayer();
545                         assert(player != NULL);
546                         player->setPosition(playerpos_f);
547                 }
548                 
549                 // Reply to server
550                 u32 replysize = 2;
551                 SharedBuffer<u8> reply(replysize);
552                 writeU16(&reply[0], TOSERVER_INIT2);
553                 // Send as reliable
554                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
555
556                 return;
557         }
558         
559         if(ser_version == SER_FMT_VER_INVALID)
560         {
561                 dout_client<<DTIME<<"WARNING: Client: Server serialization"
562                                 " format invalid or not initialized."
563                                 " Skipping incoming command="<<command<<std::endl;
564                 return;
565         }
566         
567         // Just here to avoid putting the two if's together when
568         // making some copypasta
569         {}
570
571         if(command == TOCLIENT_PLAYERPOS)
572         {
573                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
574                                 <<std::endl;
575                 /*u16 our_peer_id;
576                 {
577                         JMutexAutoLock lock(m_con_mutex);
578                         our_peer_id = m_con.GetPeerID();
579                 }
580                 // Cancel if we don't have a peer id
581                 if(our_peer_id == PEER_ID_NEW){
582                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
583                                         "we have no peer id"
584                                         <<std::endl;
585                         return;
586                 }*/
587
588                 { //envlock
589                         JMutexAutoLock envlock(m_env_mutex);
590                         
591                         u32 player_size = 2+12+12+4+4;
592                                 
593                         u32 player_count = (datasize-2) / player_size;
594                         u32 start = 2;
595                         for(u32 i=0; i<player_count; i++)
596                         {
597                                 u16 peer_id = readU16(&data[start]);
598
599                                 Player *player = m_env.getPlayer(peer_id);
600
601                                 // Skip if player doesn't exist
602                                 if(player == NULL)
603                                 {
604                                         start += player_size;
605                                         continue;
606                                 }
607
608                                 // Skip if player is local player
609                                 if(player->isLocal())
610                                 {
611                                         start += player_size;
612                                         continue;
613                                 }
614
615                                 v3s32 ps = readV3S32(&data[start+2]);
616                                 v3s32 ss = readV3S32(&data[start+2+12]);
617                                 s32 pitch_i = readS32(&data[start+2+12+12]);
618                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
619                                 /*dstream<<"Client: got "
620                                                 <<"pitch_i="<<pitch_i
621                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
622                                 f32 pitch = (f32)pitch_i / 100.0;
623                                 f32 yaw = (f32)yaw_i / 100.0;
624                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
625                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
626                                 player->setPosition(position);
627                                 player->setSpeed(speed);
628                                 player->setPitch(pitch);
629                                 player->setYaw(yaw);
630
631                                 /*dstream<<"Client: player "<<peer_id
632                                                 <<" pitch="<<pitch
633                                                 <<" yaw="<<yaw<<std::endl;*/
634
635                                 start += player_size;
636                         }
637                 } //envlock
638         }
639         else if(command == TOCLIENT_PLAYERINFO)
640         {
641                 u16 our_peer_id;
642                 {
643                         JMutexAutoLock lock(m_con_mutex);
644                         our_peer_id = m_con.GetPeerID();
645                 }
646                 // Cancel if we don't have a peer id
647                 if(our_peer_id == PEER_ID_NEW){
648                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
649                                         "we have no peer id"
650                                         <<std::endl;
651                         return;
652                 }
653                 
654                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
655
656                 { //envlock
657                         JMutexAutoLock envlock(m_env_mutex);
658                         
659                         u32 item_size = 2+PLAYERNAME_SIZE;
660                         u32 player_count = (datasize-2) / item_size;
661                         u32 start = 2;
662                         // peer_ids
663                         core::list<u16> players_alive;
664                         for(u32 i=0; i<player_count; i++)
665                         {
666                                 // Make sure the name ends in '\0'
667                                 data[start+2+20-1] = 0;
668
669                                 u16 peer_id = readU16(&data[start]);
670
671                                 players_alive.push_back(peer_id);
672                                 
673                                 /*dstream<<DTIME<<"peer_id="<<peer_id
674                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
675
676                                 // Don't update the info of the local player
677                                 if(peer_id == our_peer_id)
678                                 {
679                                         start += item_size;
680                                         continue;
681                                 }
682
683                                 Player *player = m_env.getPlayer(peer_id);
684
685                                 // Create a player if it doesn't exist
686                                 if(player == NULL)
687                                 {
688                                         player = new RemotePlayer(
689                                                         m_device->getSceneManager()->getRootSceneNode(),
690                                                         m_device,
691                                                         -1);
692                                         player->peer_id = peer_id;
693                                         m_env.addPlayer(player);
694                                         dout_client<<DTIME<<"Client: Adding new player "
695                                                         <<peer_id<<std::endl;
696                                 }
697                                 
698                                 player->updateName((char*)&data[start+2]);
699
700                                 start += item_size;
701                         }
702                         
703                         /*
704                                 Remove those players from the environment that
705                                 weren't listed by the server.
706                         */
707                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
708                         core::list<Player*> players = m_env.getPlayers();
709                         core::list<Player*>::Iterator ip;
710                         for(ip=players.begin(); ip!=players.end(); ip++)
711                         {
712                                 // Ingore local player
713                                 if((*ip)->isLocal())
714                                         continue;
715                                 
716                                 // Warn about a special case
717                                 if((*ip)->peer_id == 0)
718                                 {
719                                         dstream<<DTIME<<"WARNING: Client: Removing "
720                                                         "dead player with id=0"<<std::endl;
721                                 }
722
723                                 bool is_alive = false;
724                                 core::list<u16>::Iterator i;
725                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
726                                 {
727                                         if((*ip)->peer_id == *i)
728                                         {
729                                                 is_alive = true;
730                                                 break;
731                                         }
732                                 }
733                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
734                                                 <<" is_alive="<<is_alive<<std::endl;*/
735                                 if(is_alive)
736                                         continue;
737                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
738                                                 <<std::endl;
739                                 m_env.removePlayer((*ip)->peer_id);
740                         }
741                 } //envlock
742         }
743         else if(command == TOCLIENT_SECTORMETA)
744         {
745                 /*
746                         [0] u16 command
747                         [2] u8 sector count
748                         [3...] v2s16 pos + sector metadata
749                 */
750                 if(datasize < 3)
751                         return;
752
753                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
754
755                 { //envlock
756                         JMutexAutoLock envlock(m_env_mutex);
757                         
758                         std::string datastring((char*)&data[2], datasize-2);
759                         std::istringstream is(datastring, std::ios_base::binary);
760
761                         u8 buf[4];
762
763                         is.read((char*)buf, 1);
764                         u16 sector_count = readU8(buf);
765                         
766                         //dstream<<"sector_count="<<sector_count<<std::endl;
767
768                         for(u16 i=0; i<sector_count; i++)
769                         {
770                                 // Read position
771                                 is.read((char*)buf, 4);
772                                 v2s16 pos = readV2S16(buf);
773                                 /*dstream<<"Client: deserializing sector at "
774                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
775                                 // Create sector
776                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
777                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
778                         }
779                 } //envlock
780         }
781         else if(command == TOCLIENT_INVENTORY)
782         {
783                 if(datasize < 3)
784                         return;
785
786                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
787
788                 { //envlock
789                         //TimeTaker t2("mutex locking", m_device);
790                         JMutexAutoLock envlock(m_env_mutex);
791                         //t2.stop();
792                         
793                         //TimeTaker t3("istringstream init", m_device);
794                         std::string datastring((char*)&data[2], datasize-2);
795                         std::istringstream is(datastring, std::ios_base::binary);
796                         //t3.stop();
797                         
798                         //m_env.printPlayers(dstream);
799
800                         //TimeTaker t4("player get", m_device);
801                         Player *player = m_env.getLocalPlayer();
802                         assert(player != NULL);
803                         //t4.stop();
804
805                         //TimeTaker t1("inventory.deSerialize()", m_device);
806                         player->inventory.deSerialize(is);
807                         //t1.stop();
808
809                         m_inventory_updated = true;
810
811                         //dstream<<"Client got player inventory:"<<std::endl;
812                         //player->inventory.print(dstream);
813                 }
814         }
815         //DEBUG
816         else if(command == TOCLIENT_OBJECTDATA)
817         //else if(0)
818         {
819                 // Strip command word and create a stringstream
820                 std::string datastring((char*)&data[2], datasize-2);
821                 std::istringstream is(datastring, std::ios_base::binary);
822                 
823                 { //envlock
824                 
825                 JMutexAutoLock envlock(m_env_mutex);
826
827                 u8 buf[12];
828
829                 /*
830                         Read players
831                 */
832
833                 is.read((char*)buf, 2);
834                 u16 playercount = readU16(buf);
835                 
836                 for(u16 i=0; i<playercount; i++)
837                 {
838                         is.read((char*)buf, 2);
839                         u16 peer_id = readU16(buf);
840                         is.read((char*)buf, 12);
841                         v3s32 p_i = readV3S32(buf);
842                         is.read((char*)buf, 12);
843                         v3s32 s_i = readV3S32(buf);
844                         is.read((char*)buf, 4);
845                         s32 pitch_i = readS32(buf);
846                         is.read((char*)buf, 4);
847                         s32 yaw_i = readS32(buf);
848                         
849                         Player *player = m_env.getPlayer(peer_id);
850
851                         // Skip if player doesn't exist
852                         if(player == NULL)
853                         {
854                                 continue;
855                         }
856
857                         // Skip if player is local player
858                         if(player->isLocal())
859                         {
860                                 continue;
861                         }
862         
863                         f32 pitch = (f32)pitch_i / 100.0;
864                         f32 yaw = (f32)yaw_i / 100.0;
865                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
866                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
867                         
868                         player->setPosition(position);
869                         player->setSpeed(speed);
870                         player->setPitch(pitch);
871                         player->setYaw(yaw);
872                 }
873
874                 /*
875                         Read block objects
876                 */
877
878                 // Read active block count
879                 is.read((char*)buf, 2);
880                 u16 blockcount = readU16(buf);
881                 
882                 // Initialize delete queue with all active blocks
883                 core::map<v3s16, bool> abs_to_delete;
884                 for(core::map<v3s16, bool>::Iterator
885                                 i = m_active_blocks.getIterator();
886                                 i.atEnd() == false; i++)
887                 {
888                         v3s16 p = i.getNode()->getKey();
889                         /*dstream<<"adding "
890                                         <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
891                                         <<" to abs_to_delete"
892                                         <<std::endl;*/
893                         abs_to_delete.insert(p, true);
894                 }
895
896                 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
897                                 <<std::endl;*/
898                 
899                 for(u16 i=0; i<blockcount; i++)
900                 {
901                         // Read blockpos
902                         is.read((char*)buf, 6);
903                         v3s16 p = readV3S16(buf);
904                         // Get block from somewhere
905                         MapBlock *block = NULL;
906                         try{
907                                 block = m_env.getMap().getBlockNoCreate(p);
908                         }
909                         catch(InvalidPositionException &e)
910                         {
911                                 //TODO: Create a dummy block?
912                         }
913                         if(block == NULL)
914                         {
915                                 dstream<<"WARNING: "
916                                                 <<"Could not get block at blockpos "
917                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
918                                                 <<"in TOCLIENT_OBJECTDATA. Ignoring "
919                                                 <<"following block object data."
920                                                 <<std::endl;
921                                 return;
922                         }
923
924                         /*dstream<<"Client updating objects for block "
925                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
926                                         <<std::endl;*/
927
928                         // Insert to active block list
929                         m_active_blocks.insert(p, true);
930
931                         // Remove from deletion queue
932                         if(abs_to_delete.find(p) != NULL)
933                                 abs_to_delete.remove(p);
934
935                         // Update objects of block
936                         block->updateObjects(is, m_server_ser_ver,
937                                         m_device->getSceneManager());
938                 }
939                 
940                 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
941                                 <<std::endl;*/
942                 
943                 // Delete objects of blocks in delete queue
944                 for(core::map<v3s16, bool>::Iterator
945                                 i = abs_to_delete.getIterator();
946                                 i.atEnd() == false; i++)
947                 {
948                         v3s16 p = i.getNode()->getKey();
949                         try
950                         {
951                                 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
952                                 
953                                 // Clear objects
954                                 block->clearObjects();
955                                 // Remove from active blocks list
956                                 m_active_blocks.remove(p);
957                         }
958                         catch(InvalidPositionException &e)
959                         {
960                                 dstream<<"WARNAING: Client: "
961                                                 <<"Couldn't clear objects of active->inactive"
962                                                 <<" block "
963                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
964                                                 <<" because block was not found"
965                                                 <<std::endl;
966                                 // Ignore
967                         }
968                 }
969
970                 } //envlock
971         }
972         // Default to queueing it (for slow commands)
973         else
974         {
975                 JMutexAutoLock lock(m_incoming_queue_mutex);
976                 
977                 IncomingPacket packet(data, datasize);
978                 m_incoming_queue.push_back(packet);
979         }
980 }
981
982 /*
983         Returns true if there was something in queue
984 */
985 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
986 {
987         DSTACK(__FUNCTION_NAME);
988         
989         try //for catching con::PeerNotFoundException
990         {
991
992         con::Peer *peer;
993         {
994                 JMutexAutoLock lock(m_con_mutex);
995                 // All data is coming from the server
996                 peer = m_con.GetPeer(PEER_ID_SERVER);
997         }
998         
999         u8 ser_version = m_server_ser_ver;
1000
1001         IncomingPacket packet = getPacket();
1002         u8 *data = packet.m_data;
1003         u32 datasize = packet.m_datalen;
1004         
1005         // An empty packet means queue is empty
1006         if(data == NULL){
1007                 return false;
1008         }
1009         
1010         if(datasize < 2)
1011                 return true;
1012         
1013         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1014
1015         if(command == TOCLIENT_REMOVENODE)
1016         {
1017                 if(datasize < 8)
1018                         return true;
1019                 v3s16 p;
1020                 p.X = readS16(&data[2]);
1021                 p.Y = readS16(&data[4]);
1022                 p.Z = readS16(&data[6]);
1023                 
1024                 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1025
1026                 core::map<v3s16, MapBlock*> modified_blocks;
1027
1028                 try
1029                 {
1030                         JMutexAutoLock envlock(m_env_mutex);
1031                         //TimeTaker t("removeNodeAndUpdate", m_device);
1032                         m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1033                 }
1034                 catch(InvalidPositionException &e)
1035                 {
1036                 }
1037                 
1038                 for(core::map<v3s16, MapBlock * >::Iterator
1039                                 i = modified_blocks.getIterator();
1040                                 i.atEnd() == false; i++)
1041                 {
1042                         v3s16 p = i.getNode()->getKey();
1043                         //m_env.getMap().updateMeshes(p);
1044                         mesh_updater.add(p);
1045                 }
1046         }
1047         else if(command == TOCLIENT_ADDNODE)
1048         {
1049                 if(datasize < 8 + MapNode::serializedLength(ser_version))
1050                         return true;
1051
1052                 v3s16 p;
1053                 p.X = readS16(&data[2]);
1054                 p.Y = readS16(&data[4]);
1055                 p.Z = readS16(&data[6]);
1056                 
1057                 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1058
1059                 MapNode n;
1060                 n.deSerialize(&data[8], ser_version);
1061                 
1062                 core::map<v3s16, MapBlock*> modified_blocks;
1063
1064                 try
1065                 {
1066                         JMutexAutoLock envlock(m_env_mutex);
1067                         m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1068                 }
1069                 catch(InvalidPositionException &e)
1070                 {}
1071                 
1072                 for(core::map<v3s16, MapBlock * >::Iterator
1073                                 i = modified_blocks.getIterator();
1074                                 i.atEnd() == false; i++)
1075                 {
1076                         v3s16 p = i.getNode()->getKey();
1077                         //m_env.getMap().updateMeshes(p);
1078                         mesh_updater.add(p);
1079                 }
1080         }
1081         else if(command == TOCLIENT_BLOCKDATA)
1082         {
1083                 // Ignore too small packet
1084                 if(datasize < 8)
1085                         return true;
1086                 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1087                         goto getdata;*/
1088                         
1089                 v3s16 p;
1090                 p.X = readS16(&data[2]);
1091                 p.Y = readS16(&data[4]);
1092                 p.Z = readS16(&data[6]);
1093                 
1094                 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1095                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1096
1097                 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1098                                 <<p.X<<","<<p.Y<<","<<p.Z<<"): ";*/
1099                 
1100                 std::string datastring((char*)&data[8], datasize-8);
1101                 std::istringstream istr(datastring, std::ios_base::binary);
1102                 
1103                 MapSector *sector;
1104                 MapBlock *block;
1105                 
1106                 { //envlock
1107                         JMutexAutoLock envlock(m_env_mutex);
1108                         
1109                         v2s16 p2d(p.X, p.Z);
1110                         sector = m_env.getMap().emergeSector(p2d);
1111                         
1112                         v2s16 sp = sector->getPos();
1113                         if(sp != p2d)
1114                         {
1115                                 dstream<<"ERROR: Got sector with getPos()="
1116                                                 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1117                                                 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1118                         }
1119
1120                         assert(sp == p2d);
1121                         //assert(sector->getPos() == p2d);
1122                         
1123                         try{
1124                                 block = sector->getBlockNoCreate(p.Y);
1125                                 /*
1126                                         Update an existing block
1127                                 */
1128                                 //dstream<<"Updating"<<std::endl;
1129                                 block->deSerialize(istr, ser_version);
1130                                 //block->setChangedFlag();
1131                         }
1132                         catch(InvalidPositionException &e)
1133                         {
1134                                 /*
1135                                         Create a new block
1136                                 */
1137                                 //dstream<<"Creating new"<<std::endl;
1138                                 block = new MapBlock(&m_env.getMap(), p);
1139                                 block->deSerialize(istr, ser_version);
1140                                 sector->insertBlock(block);
1141                                 //block->setChangedFlag();
1142                         }
1143                 } //envlock
1144                 
1145                 
1146                 // Old version has zero lighting, update it.
1147                 if(ser_version == 0 || ser_version == 1)
1148                 {
1149                         derr_client<<"Client: Block in old format: "
1150                                         "Calculating lighting"<<std::endl;
1151                         core::map<v3s16, MapBlock*> blocks_changed;
1152                         blocks_changed.insert(block->getPos(), block);
1153                         core::map<v3s16, MapBlock*> modified_blocks;
1154                         m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1155                 }
1156
1157                 /*
1158                         Update Mesh of this block and blocks at x-, y- and z-
1159                 */
1160
1161                 //m_env.getMap().updateMeshes(block->getPos());
1162                 mesh_updater.add(block->getPos());
1163                 
1164                 /*
1165                         Acknowledge block.
1166                 */
1167                 /*
1168                         [0] u16 command
1169                         [2] u8 count
1170                         [3] v3s16 pos_0
1171                         [3+6] v3s16 pos_1
1172                         ...
1173                 */
1174                 u32 replysize = 2+1+6;
1175                 SharedBuffer<u8> reply(replysize);
1176                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1177                 reply[2] = 1;
1178                 writeV3S16(&reply[3], p);
1179                 // Send as reliable
1180                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1181
1182 #if 0
1183                 /*
1184                         Remove from history
1185                 */
1186                 {
1187                         JMutexAutoLock lock(m_fetchblock_mutex);
1188                         
1189                         if(m_fetchblock_history.find(p) != NULL)
1190                         {
1191                                 m_fetchblock_history.remove(p);
1192                         }
1193                         else
1194                         {
1195                                 /*
1196                                         Acknowledge block.
1197                                 */
1198                                 /*
1199                                         [0] u16 command
1200                                         [2] u8 count
1201                                         [3] v3s16 pos_0
1202                                         [3+6] v3s16 pos_1
1203                                         ...
1204                                 */
1205                                 u32 replysize = 2+1+6;
1206                                 SharedBuffer<u8> reply(replysize);
1207                                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1208                                 reply[2] = 1;
1209                                 writeV3S16(&reply[3], p);
1210                                 // Send as reliable
1211                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1212                         }
1213                 }
1214 #endif
1215         }
1216         else
1217         {
1218                 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1219                                 <<command<<std::endl;
1220         }
1221
1222         return true;
1223
1224         } //try
1225         catch(con::PeerNotFoundException &e)
1226         {
1227                 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1228                                 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1229                 return false;
1230         }
1231 }
1232
1233 bool Client::AsyncProcessData()
1234 {
1235         for(;;)
1236         {
1237                 // We want to update the meshes as soon as a single packet has
1238                 // been processed
1239                 LazyMeshUpdater mesh_updater(&m_env);
1240                 bool r = AsyncProcessPacket(mesh_updater);
1241                 if(r == false)
1242                         break;
1243         }
1244         return false;
1245
1246         /*LazyMeshUpdater mesh_updater(&m_env);
1247         for(;;)
1248         {
1249                 bool r = AsyncProcessPacket(mesh_updater);
1250                 if(r == false)
1251                         break;
1252         }
1253         return false;*/
1254
1255 }
1256
1257 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1258 {
1259         JMutexAutoLock lock(m_con_mutex);
1260         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1261 }
1262
1263 #if 0
1264 void Client::fetchBlock(v3s16 p, u8 flags)
1265 {
1266         if(connectedAndInitialized() == false)
1267                 throw ClientNotReadyException
1268                 ("ClientNotReadyException: connectedAndInitialized() == false");
1269
1270         /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1271                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1272
1273         JMutexAutoLock conlock(m_con_mutex);
1274
1275         SharedBuffer<u8> data(9);
1276         writeU16(&data[0], TOSERVER_GETBLOCK);
1277         writeS16(&data[2], p.X);
1278         writeS16(&data[4], p.Y);
1279         writeS16(&data[6], p.Z);
1280         writeU8(&data[8], flags);
1281         m_con.Send(PEER_ID_SERVER, 1, data, true);
1282 }
1283
1284 /*
1285         Calls fetchBlock() on some nearby missing blocks.
1286
1287         Returns when any of various network load indicators go over limit.
1288
1289         Does nearly the same thing as the old updateChangedVisibleArea()
1290 */
1291 void Client::fetchBlocks()
1292 {
1293         if(connectedAndInitialized() == false)
1294                 throw ClientNotReadyException
1295                 ("ClientNotReadyException: connectedAndInitialized() == false");
1296 }
1297 #endif
1298
1299 bool Client::isFetchingBlocks()
1300 {
1301         JMutexAutoLock conlock(m_con_mutex);
1302         con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1303         // Not really fetching but can't fetch more.
1304         if(peer == NULL) return true;
1305
1306         con::Channel *channel = &(peer->channels[1]);
1307         /*
1308                 NOTE: Channel 0 should always be used for fetching blocks,
1309                       and for nothing else.
1310         */
1311         if(channel->incoming_reliables.size() > 0)
1312                 return true;
1313         if(channel->outgoing_reliables.size() > 0)
1314                 return true;
1315         return false;
1316 }
1317
1318 IncomingPacket Client::getPacket()
1319 {
1320         JMutexAutoLock lock(m_incoming_queue_mutex);
1321         
1322         core::list<IncomingPacket>::Iterator i;
1323         // Refer to first one
1324         i = m_incoming_queue.begin();
1325
1326         // If queue is empty, return empty packet
1327         if(i == m_incoming_queue.end()){
1328                 IncomingPacket packet;
1329                 return packet;
1330         }
1331         
1332         // Pop out first packet and return it
1333         IncomingPacket packet = *i;
1334         m_incoming_queue.erase(i);
1335         return packet;
1336 }
1337
1338 #if 0
1339 void Client::removeNode(v3s16 nodepos)
1340 {
1341         if(connectedAndInitialized() == false){
1342                 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1343                                 <<std::endl;
1344                 return;
1345         }
1346         
1347         // Test that the position exists
1348         try{
1349                 JMutexAutoLock envlock(m_env_mutex);
1350                 m_env.getMap().getNode(nodepos);
1351         }
1352         catch(InvalidPositionException &e)
1353         {
1354                 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1355                                 <<std::endl;
1356                 return;
1357         }
1358
1359         SharedBuffer<u8> data(8);
1360         writeU16(&data[0], TOSERVER_REMOVENODE);
1361         writeS16(&data[2], nodepos.X);
1362         writeS16(&data[4], nodepos.Y);
1363         writeS16(&data[6], nodepos.Z);
1364         Send(0, data, true);
1365 }
1366
1367 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1368 {
1369         if(connectedAndInitialized() == false){
1370                 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1371                                 "cancelled (not connected)"
1372                                 <<std::endl;
1373                 return;
1374         }
1375         
1376         // Test that the position exists
1377         try{
1378                 JMutexAutoLock envlock(m_env_mutex);
1379                 m_env.getMap().getNode(nodepos);
1380         }
1381         catch(InvalidPositionException &e)
1382         {
1383                 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1384                                 <<std::endl;
1385                 return;
1386         }
1387
1388         //u8 ser_version = m_server_ser_ver;
1389
1390         // SUGGESTION: The validity of the operation could be checked here too
1391
1392         u8 datasize = 2 + 6 + 2;
1393         SharedBuffer<u8> data(datasize);
1394         writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1395         writeS16(&data[2], nodepos.X);
1396         writeS16(&data[4], nodepos.Y);
1397         writeS16(&data[6], nodepos.Z);
1398         writeU16(&data[8], i);
1399         Send(0, data, true);
1400 }
1401 #endif
1402
1403 void Client::clickGround(u8 button, v3s16 nodepos_undersurface,
1404                 v3s16 nodepos_oversurface, u16 item)
1405 {
1406         if(connectedAndInitialized() == false){
1407                 dout_client<<DTIME<<"Client::clickGround() "
1408                                 "cancelled (not connected)"
1409                                 <<std::endl;
1410                 return;
1411         }
1412         
1413         /*
1414                 length: 19
1415                 [0] u16 command
1416                 [2] u8 button (0=left, 1=right)
1417                 [3] v3s16 nodepos_undersurface
1418                 [9] v3s16 nodepos_abovesurface
1419                 [15] u16 item
1420         */
1421         u8 datasize = 2 + 1 + 6 + 6 + 2;
1422         SharedBuffer<u8> data(datasize);
1423         writeU16(&data[0], TOSERVER_CLICK_GROUND);
1424         writeU8(&data[2], button);
1425         writeV3S16(&data[3], nodepos_undersurface);
1426         writeV3S16(&data[9], nodepos_oversurface);
1427         writeU16(&data[15], item);
1428         Send(0, data, true);
1429 }
1430
1431 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1432 {
1433         if(connectedAndInitialized() == false){
1434                 dout_client<<DTIME<<"Client::clickObject() "
1435                                 "cancelled (not connected)"
1436                                 <<std::endl;
1437                 return;
1438         }
1439         
1440         /*
1441                 [0] u16 command
1442                 [2] u8 button (0=left, 1=right)
1443                 [3] v3s16 block
1444                 [9] s16 id
1445                 [11] u16 item
1446         */
1447         u8 datasize = 2 + 1 + 6 + 2 + 2;
1448         SharedBuffer<u8> data(datasize);
1449         writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1450         writeU8(&data[2], button);
1451         writeV3S16(&data[3], blockpos);
1452         writeS16(&data[9], id);
1453         writeU16(&data[11], item);
1454         Send(0, data, true);
1455 }
1456
1457 void Client::release(u8 button)
1458 {
1459         //TODO
1460 }
1461
1462 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1463 {
1464         /*
1465                 u16 command
1466                 v3s16 blockpos
1467                 s16 id
1468                 u16 textlen
1469                 textdata
1470         */
1471         std::ostringstream os(std::ios_base::binary);
1472         u8 buf[12];
1473         
1474         // Write command
1475         writeU16(buf, TOSERVER_SIGNTEXT);
1476         os.write((char*)buf, 2);
1477         
1478         // Write blockpos
1479         writeV3S16(buf, blockpos);
1480         os.write((char*)buf, 6);
1481
1482         // Write id
1483         writeS16(buf, id);
1484         os.write((char*)buf, 2);
1485
1486         u16 textlen = text.size();
1487         // Write text length
1488         writeS16(buf, textlen);
1489         os.write((char*)buf, 2);
1490
1491         // Write text
1492         os.write((char*)text.c_str(), textlen);
1493         
1494         // Make data buffer
1495         std::string s = os.str();
1496         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1497         // Send as reliable
1498         Send(0, data, true);
1499 }
1500
1501 void Client::sendPlayerPos()
1502 {
1503         JMutexAutoLock envlock(m_env_mutex);
1504         
1505         Player *myplayer = m_env.getLocalPlayer();
1506         if(myplayer == NULL)
1507                 return;
1508         
1509         u16 our_peer_id;
1510         {
1511                 JMutexAutoLock lock(m_con_mutex);
1512                 our_peer_id = m_con.GetPeerID();
1513         }
1514         
1515         // Set peer id if not set already
1516         if(myplayer->peer_id == PEER_ID_NEW)
1517                 myplayer->peer_id = our_peer_id;
1518         // Check that an existing peer_id is the same as the connection's
1519         assert(myplayer->peer_id == our_peer_id);
1520         
1521         v3f pf = myplayer->getPosition();
1522         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1523         v3f sf = myplayer->getSpeed();
1524         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1525         s32 pitch = myplayer->getPitch() * 100;
1526         s32 yaw = myplayer->getYaw() * 100;
1527
1528         /*
1529                 Format:
1530                 [0] u16 command
1531                 [2] v3s32 position*100
1532                 [2+12] v3s32 speed*100
1533                 [2+12+12] s32 pitch*100
1534                 [2+12+12+4] s32 yaw*100
1535         */
1536
1537         SharedBuffer<u8> data(2+12+12+4+4);
1538         writeU16(&data[0], TOSERVER_PLAYERPOS);
1539         writeV3S32(&data[2], position);
1540         writeV3S32(&data[2+12], speed);
1541         writeS32(&data[2+12+12], pitch);
1542         writeS32(&data[2+12+12+4], yaw);
1543
1544         // Send as unreliable
1545         Send(0, data, false);
1546 }
1547
1548
1549 void Client::updateCamera(v3f pos, v3f dir)
1550 {
1551         m_env.getMap().updateCamera(pos, dir);
1552         camera_position = pos;
1553         camera_direction = dir;
1554 }
1555
1556 MapNode Client::getNode(v3s16 p)
1557 {
1558         JMutexAutoLock envlock(m_env_mutex);
1559         return m_env.getMap().getNode(p);
1560 }
1561
1562 /*f32 Client::getGroundHeight(v2s16 p)
1563 {
1564         JMutexAutoLock envlock(m_env_mutex);
1565         return m_env.getMap().getGroundHeight(p);
1566 }*/
1567
1568 bool Client::isNodeUnderground(v3s16 p)
1569 {
1570         JMutexAutoLock envlock(m_env_mutex);
1571         return m_env.getMap().isNodeUnderground(p);
1572 }
1573
1574 /*Player * Client::getLocalPlayer()
1575 {
1576         JMutexAutoLock envlock(m_env_mutex);
1577         return m_env.getLocalPlayer();
1578 }*/
1579
1580 /*core::list<Player*> Client::getPlayers()
1581 {
1582         JMutexAutoLock envlock(m_env_mutex);
1583         return m_env.getPlayers();
1584 }*/
1585
1586 v3f Client::getPlayerPosition()
1587 {
1588         JMutexAutoLock envlock(m_env_mutex);
1589         LocalPlayer *player = m_env.getLocalPlayer();
1590         assert(player != NULL);
1591         return player->getPosition();
1592 }
1593
1594 void Client::setPlayerControl(PlayerControl &control)
1595 {
1596         JMutexAutoLock envlock(m_env_mutex);
1597         LocalPlayer *player = m_env.getLocalPlayer();
1598         assert(player != NULL);
1599         player->control = control;
1600 }
1601
1602 // Returns true if the inventory of the local player has been
1603 // updated from the server. If it is true, it is set to false.
1604 bool Client::getLocalInventoryUpdated()
1605 {
1606         // m_inventory_updated is behind envlock
1607         JMutexAutoLock envlock(m_env_mutex);
1608         bool updated = m_inventory_updated;
1609         m_inventory_updated = false;
1610         return updated;
1611 }
1612
1613 // Copies the inventory of the local player to parameter
1614 void Client::getLocalInventory(Inventory &dst)
1615 {
1616         JMutexAutoLock envlock(m_env_mutex);
1617         Player *player = m_env.getLocalPlayer();
1618         assert(player != NULL);
1619         dst = player->inventory;
1620 }
1621
1622 MapBlockObject * Client::getSelectedObject(
1623                 f32 max_d,
1624                 v3f from_pos_f_on_map,
1625                 core::line3d<f32> shootline_on_map
1626         )
1627 {
1628         JMutexAutoLock envlock(m_env_mutex);
1629
1630         core::array<DistanceSortedObject> objects;
1631
1632         for(core::map<v3s16, bool>::Iterator
1633                         i = m_active_blocks.getIterator();
1634                         i.atEnd() == false; i++)
1635         {
1636                 v3s16 p = i.getNode()->getKey();
1637
1638                 MapBlock *block = NULL;
1639                 try
1640                 {
1641                         block = m_env.getMap().getBlockNoCreate(p);
1642                 }
1643                 catch(InvalidPositionException &e)
1644                 {
1645                         continue;
1646                 }
1647
1648                 // Calculate from_pos relative to block
1649                 v3s16 block_pos_i_on_map = block->getPosRelative();
1650                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1651                 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1652
1653                 block->getObjects(from_pos_f_on_block, max_d, objects);
1654                 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1655         }
1656
1657         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1658         
1659         // Sort them.
1660         // After this, the closest object is the first in the array.
1661         objects.sort();
1662
1663         for(u32 i=0; i<objects.size(); i++)
1664         {
1665                 MapBlockObject *obj = objects[i].obj;
1666                 MapBlock *block = obj->getBlock();
1667
1668                 // Calculate shootline relative to block
1669                 v3s16 block_pos_i_on_map = block->getPosRelative();
1670                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1671                 core::line3d<f32> shootline_on_block(
1672                                 shootline_on_map.start - block_pos_f_on_map,
1673                                 shootline_on_map.end - block_pos_f_on_map
1674                 );
1675
1676                 if(obj->isSelected(shootline_on_block))
1677                 {
1678                         //dstream<<"Returning selected object"<<std::endl;
1679                         return obj;
1680                 }
1681         }
1682
1683         //dstream<<"No object selected; returning NULL."<<std::endl;
1684         return NULL;
1685 }
1686
1687 void Client::printDebugInfo(std::ostream &os)
1688 {
1689         //JMutexAutoLock lock1(m_fetchblock_mutex);
1690         JMutexAutoLock lock2(m_incoming_queue_mutex);
1691
1692         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1693                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1694                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1695                 <<std::endl;
1696 }
1697         
1698