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