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