]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client.cpp
4979829d1cfd90f70ef4f90cfdc6f8f0bc3c161f
[dragonfireclient.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         /*{
402                 JMutexAutoLock lock(m_step_dtime_mutex);
403                 m_step_dtime += dtime;
404         }*/
405 }
406
407 float Client::asyncStep()
408 {
409         DSTACK(__FUNCTION_NAME);
410         //dstream<<"Client::asyncStep()"<<std::endl;
411         
412         /*float dtime;
413         {
414                 JMutexAutoLock lock1(m_step_dtime_mutex);
415                 if(m_step_dtime < 0.001)
416                         return 0.0;
417                 dtime = m_step_dtime;
418                 m_step_dtime = 0.0;
419         }
420
421         return dtime;*/
422         return 0.0;
423 }
424
425 // Virtual methods from con::PeerHandler
426 void Client::peerAdded(con::Peer *peer)
427 {
428         derr_client<<"Client::peerAdded(): peer->id="
429                         <<peer->id<<std::endl;
430 }
431 void Client::deletingPeer(con::Peer *peer, bool timeout)
432 {
433         derr_client<<"Client::deletingPeer(): "
434                         "Server Peer is getting deleted "
435                         <<"(timeout="<<timeout<<")"<<std::endl;
436 }
437
438 void Client::ReceiveAll()
439 {
440         DSTACK(__FUNCTION_NAME);
441         for(;;)
442         {
443                 try{
444                         Receive();
445                 }
446                 catch(con::NoIncomingDataException &e)
447                 {
448                         break;
449                 }
450                 catch(con::InvalidIncomingDataException &e)
451                 {
452                         dout_client<<DTIME<<"Client::ReceiveAll(): "
453                                         "InvalidIncomingDataException: what()="
454                                         <<e.what()<<std::endl;
455                 }
456                 //TODO: Testing
457                 //break;
458         }
459 }
460
461 void Client::Receive()
462 {
463         DSTACK(__FUNCTION_NAME);
464         u32 data_maxsize = 10000;
465         Buffer<u8> data(data_maxsize);
466         u16 sender_peer_id;
467         u32 datasize;
468         {
469                 //TimeTaker t1("con mutex and receive", m_device);
470                 JMutexAutoLock lock(m_con_mutex);
471                 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
472         }
473         //TimeTaker t1("ProcessData", m_device);
474         ProcessData(*data, datasize, sender_peer_id);
475 }
476
477 /*
478         sender_peer_id given to this shall be quaranteed to be a valid peer
479 */
480 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
481 {
482         DSTACK(__FUNCTION_NAME);
483
484         // Ignore packets that don't even fit a command
485         if(datasize < 2)
486         {
487                 m_packetcounter.add(60000);
488                 return;
489         }
490
491         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
492
493         //dstream<<"Client: received command="<<command<<std::endl;
494         m_packetcounter.add((u16)command);
495         
496         /*
497                 If this check is removed, be sure to change the queue
498                 system to know the ids
499         */
500         if(sender_peer_id != PEER_ID_SERVER)
501         {
502                 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
503                                 "coming from server: peer_id="<<sender_peer_id
504                                 <<std::endl;
505                 return;
506         }
507
508         con::Peer *peer;
509         {
510                 JMutexAutoLock lock(m_con_mutex);
511                 // All data is coming from the server
512                 // PeerNotFoundException is handled by caller.
513                 peer = m_con.GetPeer(PEER_ID_SERVER);
514         }
515
516         u8 ser_version = m_server_ser_ver;
517
518         //dstream<<"Client received command="<<(int)command<<std::endl;
519
520         // Execute fast commands straight away
521
522         if(command == TOCLIENT_INIT)
523         {
524                 if(datasize < 3)
525                         return;
526
527                 u8 deployed = data[2];
528
529                 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
530                                 "deployed="<<((int)deployed&0xff)<<std::endl;
531
532                 if(deployed < SER_FMT_VER_LOWEST
533                                 || deployed > SER_FMT_VER_HIGHEST)
534                 {
535                         derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
536                                         <<"unsupported ser_fmt_ver"<<std::endl;
537                         return;
538                 }
539                 
540                 m_server_ser_ver = deployed;
541
542                 // Get player position
543                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
544                 if(datasize >= 2+1+6)
545                         playerpos_s16 = readV3S16(&data[2+1]);
546                 v3f playerpos_f = intToFloat(playerpos_s16) - v3f(0, BS/2, 0);
547
548                 { //envlock
549                         JMutexAutoLock envlock(m_env_mutex);
550                         
551                         // Set player position
552                         Player *player = m_env.getLocalPlayer();
553                         assert(player != NULL);
554                         player->setPosition(playerpos_f);
555                 }
556                 
557                 // Reply to server
558                 u32 replysize = 2;
559                 SharedBuffer<u8> reply(replysize);
560                 writeU16(&reply[0], TOSERVER_INIT2);
561                 // Send as reliable
562                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
563
564                 return;
565         }
566         
567         if(ser_version == SER_FMT_VER_INVALID)
568         {
569                 dout_client<<DTIME<<"WARNING: Client: Server serialization"
570                                 " format invalid or not initialized."
571                                 " Skipping incoming command="<<command<<std::endl;
572                 return;
573         }
574         
575         // Just here to avoid putting the two if's together when
576         // making some copypasta
577         {}
578
579         if(command == TOCLIENT_PLAYERPOS)
580         {
581                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
582                                 <<std::endl;
583                 /*u16 our_peer_id;
584                 {
585                         JMutexAutoLock lock(m_con_mutex);
586                         our_peer_id = m_con.GetPeerID();
587                 }
588                 // Cancel if we don't have a peer id
589                 if(our_peer_id == PEER_ID_NEW){
590                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
591                                         "we have no peer id"
592                                         <<std::endl;
593                         return;
594                 }*/
595
596                 { //envlock
597                         JMutexAutoLock envlock(m_env_mutex);
598                         
599                         u32 player_size = 2+12+12+4+4;
600                                 
601                         u32 player_count = (datasize-2) / player_size;
602                         u32 start = 2;
603                         for(u32 i=0; i<player_count; i++)
604                         {
605                                 u16 peer_id = readU16(&data[start]);
606
607                                 Player *player = m_env.getPlayer(peer_id);
608
609                                 // Skip if player doesn't exist
610                                 if(player == NULL)
611                                 {
612                                         start += player_size;
613                                         continue;
614                                 }
615
616                                 // Skip if player is local player
617                                 if(player->isLocal())
618                                 {
619                                         start += player_size;
620                                         continue;
621                                 }
622
623                                 v3s32 ps = readV3S32(&data[start+2]);
624                                 v3s32 ss = readV3S32(&data[start+2+12]);
625                                 s32 pitch_i = readS32(&data[start+2+12+12]);
626                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
627                                 /*dstream<<"Client: got "
628                                                 <<"pitch_i="<<pitch_i
629                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
630                                 f32 pitch = (f32)pitch_i / 100.0;
631                                 f32 yaw = (f32)yaw_i / 100.0;
632                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
633                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
634                                 player->setPosition(position);
635                                 player->setSpeed(speed);
636                                 player->setPitch(pitch);
637                                 player->setYaw(yaw);
638
639                                 /*dstream<<"Client: player "<<peer_id
640                                                 <<" pitch="<<pitch
641                                                 <<" yaw="<<yaw<<std::endl;*/
642
643                                 start += player_size;
644                         }
645                 } //envlock
646         }
647         else if(command == TOCLIENT_PLAYERINFO)
648         {
649                 u16 our_peer_id;
650                 {
651                         JMutexAutoLock lock(m_con_mutex);
652                         our_peer_id = m_con.GetPeerID();
653                 }
654                 // Cancel if we don't have a peer id
655                 if(our_peer_id == PEER_ID_NEW){
656                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
657                                         "we have no peer id"
658                                         <<std::endl;
659                         return;
660                 }
661                 
662                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
663
664                 { //envlock
665                         JMutexAutoLock envlock(m_env_mutex);
666                         
667                         u32 item_size = 2+PLAYERNAME_SIZE;
668                         u32 player_count = (datasize-2) / item_size;
669                         u32 start = 2;
670                         // peer_ids
671                         core::list<u16> players_alive;
672                         for(u32 i=0; i<player_count; i++)
673                         {
674                                 // Make sure the name ends in '\0'
675                                 data[start+2+20-1] = 0;
676
677                                 u16 peer_id = readU16(&data[start]);
678
679                                 players_alive.push_back(peer_id);
680                                 
681                                 /*dstream<<DTIME<<"peer_id="<<peer_id
682                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
683
684                                 // Don't update the info of the local player
685                                 if(peer_id == our_peer_id)
686                                 {
687                                         start += item_size;
688                                         continue;
689                                 }
690
691                                 Player *player = m_env.getPlayer(peer_id);
692
693                                 // Create a player if it doesn't exist
694                                 if(player == NULL)
695                                 {
696                                         player = new RemotePlayer(
697                                                         m_device->getSceneManager()->getRootSceneNode(),
698                                                         m_device,
699                                                         -1);
700                                         player->peer_id = peer_id;
701                                         m_env.addPlayer(player);
702                                         dout_client<<DTIME<<"Client: Adding new player "
703                                                         <<peer_id<<std::endl;
704                                 }
705                                 
706                                 player->updateName((char*)&data[start+2]);
707
708                                 start += item_size;
709                         }
710                         
711                         /*
712                                 Remove those players from the environment that
713                                 weren't listed by the server.
714                         */
715                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
716                         core::list<Player*> players = m_env.getPlayers();
717                         core::list<Player*>::Iterator ip;
718                         for(ip=players.begin(); ip!=players.end(); ip++)
719                         {
720                                 // Ingore local player
721                                 if((*ip)->isLocal())
722                                         continue;
723                                 
724                                 // Warn about a special case
725                                 if((*ip)->peer_id == 0)
726                                 {
727                                         dstream<<DTIME<<"WARNING: Client: Removing "
728                                                         "dead player with id=0"<<std::endl;
729                                 }
730
731                                 bool is_alive = false;
732                                 core::list<u16>::Iterator i;
733                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
734                                 {
735                                         if((*ip)->peer_id == *i)
736                                         {
737                                                 is_alive = true;
738                                                 break;
739                                         }
740                                 }
741                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
742                                                 <<" is_alive="<<is_alive<<std::endl;*/
743                                 if(is_alive)
744                                         continue;
745                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
746                                                 <<std::endl;
747                                 m_env.removePlayer((*ip)->peer_id);
748                         }
749                 } //envlock
750         }
751         else if(command == TOCLIENT_SECTORMETA)
752         {
753                 /*
754                         [0] u16 command
755                         [2] u8 sector count
756                         [3...] v2s16 pos + sector metadata
757                 */
758                 if(datasize < 3)
759                         return;
760
761                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
762
763                 { //envlock
764                         JMutexAutoLock envlock(m_env_mutex);
765                         
766                         std::string datastring((char*)&data[2], datasize-2);
767                         std::istringstream is(datastring, std::ios_base::binary);
768
769                         u8 buf[4];
770
771                         is.read((char*)buf, 1);
772                         u16 sector_count = readU8(buf);
773                         
774                         //dstream<<"sector_count="<<sector_count<<std::endl;
775
776                         for(u16 i=0; i<sector_count; i++)
777                         {
778                                 // Read position
779                                 is.read((char*)buf, 4);
780                                 v2s16 pos = readV2S16(buf);
781                                 /*dstream<<"Client: deserializing sector at "
782                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
783                                 // Create sector
784                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
785                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
786                         }
787                 } //envlock
788         }
789         else if(command == TOCLIENT_INVENTORY)
790         {
791                 if(datasize < 3)
792                         return;
793
794                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
795
796                 { //envlock
797                         //TimeTaker t2("mutex locking", m_device);
798                         JMutexAutoLock envlock(m_env_mutex);
799                         //t2.stop();
800                         
801                         //TimeTaker t3("istringstream init", m_device);
802                         std::string datastring((char*)&data[2], datasize-2);
803                         std::istringstream is(datastring, std::ios_base::binary);
804                         //t3.stop();
805                         
806                         //m_env.printPlayers(dstream);
807
808                         //TimeTaker t4("player get", m_device);
809                         Player *player = m_env.getLocalPlayer();
810                         assert(player != NULL);
811                         //t4.stop();
812
813                         //TimeTaker t1("inventory.deSerialize()", m_device);
814                         player->inventory.deSerialize(is);
815                         //t1.stop();
816
817                         m_inventory_updated = true;
818
819                         //dstream<<"Client got player inventory:"<<std::endl;
820                         //player->inventory.print(dstream);
821                 }
822         }
823         //DEBUG
824         else if(command == TOCLIENT_OBJECTDATA)
825         //else if(0)
826         {
827                 // Strip command word and create a stringstream
828                 std::string datastring((char*)&data[2], datasize-2);
829                 std::istringstream is(datastring, std::ios_base::binary);
830                 
831                 { //envlock
832                 
833                 JMutexAutoLock envlock(m_env_mutex);
834
835                 u8 buf[12];
836
837                 /*
838                         Read players
839                 */
840
841                 is.read((char*)buf, 2);
842                 u16 playercount = readU16(buf);
843                 
844                 for(u16 i=0; i<playercount; i++)
845                 {
846                         is.read((char*)buf, 2);
847                         u16 peer_id = readU16(buf);
848                         is.read((char*)buf, 12);
849                         v3s32 p_i = readV3S32(buf);
850                         is.read((char*)buf, 12);
851                         v3s32 s_i = readV3S32(buf);
852                         is.read((char*)buf, 4);
853                         s32 pitch_i = readS32(buf);
854                         is.read((char*)buf, 4);
855                         s32 yaw_i = readS32(buf);
856                         
857                         Player *player = m_env.getPlayer(peer_id);
858
859                         // Skip if player doesn't exist
860                         if(player == NULL)
861                         {
862                                 continue;
863                         }
864
865                         // Skip if player is local player
866                         if(player->isLocal())
867                         {
868                                 continue;
869                         }
870         
871                         f32 pitch = (f32)pitch_i / 100.0;
872                         f32 yaw = (f32)yaw_i / 100.0;
873                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
874                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
875                         
876                         player->setPosition(position);
877                         player->setSpeed(speed);
878                         player->setPitch(pitch);
879                         player->setYaw(yaw);
880                 }
881
882                 /*
883                         Read block objects
884                 */
885
886                 // Read active block count
887                 is.read((char*)buf, 2);
888                 u16 blockcount = readU16(buf);
889                 
890                 // Initialize delete queue with all active blocks
891                 core::map<v3s16, bool> abs_to_delete;
892                 for(core::map<v3s16, bool>::Iterator
893                                 i = m_active_blocks.getIterator();
894                                 i.atEnd() == false; i++)
895                 {
896                         v3s16 p = i.getNode()->getKey();
897                         /*dstream<<"adding "
898                                         <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
899                                         <<" to abs_to_delete"
900                                         <<std::endl;*/
901                         abs_to_delete.insert(p, true);
902                 }
903
904                 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
905                                 <<std::endl;*/
906                 
907                 for(u16 i=0; i<blockcount; i++)
908                 {
909                         // Read blockpos
910                         is.read((char*)buf, 6);
911                         v3s16 p = readV3S16(buf);
912                         // Get block from somewhere
913                         MapBlock *block = NULL;
914                         try{
915                                 block = m_env.getMap().getBlockNoCreate(p);
916                         }
917                         catch(InvalidPositionException &e)
918                         {
919                                 //TODO: Create a dummy block?
920                         }
921                         if(block == NULL)
922                         {
923                                 dstream<<"WARNING: "
924                                                 <<"Could not get block at blockpos "
925                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
926                                                 <<"in TOCLIENT_OBJECTDATA. Ignoring "
927                                                 <<"following block object data."
928                                                 <<std::endl;
929                                 return;
930                         }
931
932                         /*dstream<<"Client updating objects for block "
933                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
934                                         <<std::endl;*/
935
936                         // Insert to active block list
937                         m_active_blocks.insert(p, true);
938
939                         // Remove from deletion queue
940                         if(abs_to_delete.find(p) != NULL)
941                                 abs_to_delete.remove(p);
942
943                         /*
944                                 Update objects of block
945                                 
946                                 NOTE: Be sure this is done in the main thread.
947                         */
948                         block->updateObjects(is, m_server_ser_ver,
949                                         m_device->getSceneManager());
950                 }
951                 
952                 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
953                                 <<std::endl;*/
954                 
955                 // Delete objects of blocks in delete queue
956                 for(core::map<v3s16, bool>::Iterator
957                                 i = abs_to_delete.getIterator();
958                                 i.atEnd() == false; i++)
959                 {
960                         v3s16 p = i.getNode()->getKey();
961                         try
962                         {
963                                 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
964                                 
965                                 // Clear objects
966                                 block->clearObjects();
967                                 // Remove from active blocks list
968                                 m_active_blocks.remove(p);
969                         }
970                         catch(InvalidPositionException &e)
971                         {
972                                 dstream<<"WARNAING: Client: "
973                                                 <<"Couldn't clear objects of active->inactive"
974                                                 <<" block "
975                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
976                                                 <<" because block was not found"
977                                                 <<std::endl;
978                                 // Ignore
979                         }
980                 }
981
982                 } //envlock
983         }
984         else if(command == TOCLIENT_TIME_OF_DAY)
985         {
986                 if(datasize < 4)
987                         return;
988                 
989                 u16 time = readU16(&data[2]);
990                 time = time % 24000;
991                 m_time_of_day.set(time);
992                 //dstream<<"Client: time="<<time<<std::endl;
993         }
994         // Default to queueing it (for slow commands)
995         else
996         {
997                 JMutexAutoLock lock(m_incoming_queue_mutex);
998                 
999                 IncomingPacket packet(data, datasize);
1000                 m_incoming_queue.push_back(packet);
1001         }
1002 }
1003
1004 /*
1005         Returns true if there was something in queue
1006 */
1007 bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
1008 {
1009         DSTACK(__FUNCTION_NAME);
1010         
1011         try //for catching con::PeerNotFoundException
1012         {
1013
1014         con::Peer *peer;
1015         {
1016                 JMutexAutoLock lock(m_con_mutex);
1017                 // All data is coming from the server
1018                 peer = m_con.GetPeer(PEER_ID_SERVER);
1019         }
1020         
1021         u8 ser_version = m_server_ser_ver;
1022
1023         IncomingPacket packet = getPacket();
1024         u8 *data = packet.m_data;
1025         u32 datasize = packet.m_datalen;
1026         
1027         // An empty packet means queue is empty
1028         if(data == NULL){
1029                 return false;
1030         }
1031         
1032         if(datasize < 2)
1033                 return true;
1034         
1035         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1036
1037         if(command == TOCLIENT_REMOVENODE)
1038         {
1039                 if(datasize < 8)
1040                         return true;
1041                 v3s16 p;
1042                 p.X = readS16(&data[2]);
1043                 p.Y = readS16(&data[4]);
1044                 p.Z = readS16(&data[6]);
1045                 
1046                 //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
1047
1048                 core::map<v3s16, MapBlock*> modified_blocks;
1049
1050                 try
1051                 {
1052                         JMutexAutoLock envlock(m_env_mutex);
1053                         //TimeTaker t("removeNodeAndUpdate", m_device);
1054                         m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1055                 }
1056                 catch(InvalidPositionException &e)
1057                 {
1058                 }
1059                 
1060                 for(core::map<v3s16, MapBlock * >::Iterator
1061                                 i = modified_blocks.getIterator();
1062                                 i.atEnd() == false; i++)
1063                 {
1064                         v3s16 p = i.getNode()->getKey();
1065                         //m_env.getMap().updateMeshes(p);
1066                         mesh_updater.add(p);
1067                 }
1068         }
1069         else if(command == TOCLIENT_ADDNODE)
1070         {
1071                 if(datasize < 8 + MapNode::serializedLength(ser_version))
1072                         return true;
1073
1074                 v3s16 p;
1075                 p.X = readS16(&data[2]);
1076                 p.Y = readS16(&data[4]);
1077                 p.Z = readS16(&data[6]);
1078                 
1079                 //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
1080
1081                 MapNode n;
1082                 n.deSerialize(&data[8], ser_version);
1083                 
1084                 core::map<v3s16, MapBlock*> modified_blocks;
1085
1086                 try
1087                 {
1088                         JMutexAutoLock envlock(m_env_mutex);
1089                         m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1090                 }
1091                 catch(InvalidPositionException &e)
1092                 {}
1093                 
1094                 for(core::map<v3s16, MapBlock * >::Iterator
1095                                 i = modified_blocks.getIterator();
1096                                 i.atEnd() == false; i++)
1097                 {
1098                         v3s16 p = i.getNode()->getKey();
1099                         //m_env.getMap().updateMeshes(p);
1100                         mesh_updater.add(p);
1101                 }
1102         }
1103         else if(command == TOCLIENT_BLOCKDATA)
1104         {
1105                 // Ignore too small packet
1106                 if(datasize < 8)
1107                         return true;
1108                 /*if(datasize < 8 + MapBlock::serializedLength(ser_version))
1109                         goto getdata;*/
1110                         
1111                 v3s16 p;
1112                 p.X = readS16(&data[2]);
1113                 p.Y = readS16(&data[4]);
1114                 p.Z = readS16(&data[6]);
1115                 
1116                 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
1117                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1118
1119                 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
1120                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1121                 
1122                 std::string datastring((char*)&data[8], datasize-8);
1123                 std::istringstream istr(datastring, std::ios_base::binary);
1124                 
1125                 MapSector *sector;
1126                 MapBlock *block;
1127                 
1128                 { //envlock
1129                         JMutexAutoLock envlock(m_env_mutex);
1130                         
1131                         v2s16 p2d(p.X, p.Z);
1132                         sector = m_env.getMap().emergeSector(p2d);
1133                         
1134                         v2s16 sp = sector->getPos();
1135                         if(sp != p2d)
1136                         {
1137                                 dstream<<"ERROR: Got sector with getPos()="
1138                                                 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
1139                                                 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
1140                         }
1141
1142                         assert(sp == p2d);
1143                         //assert(sector->getPos() == p2d);
1144                         
1145                         try{
1146                                 block = sector->getBlockNoCreate(p.Y);
1147                                 /*
1148                                         Update an existing block
1149                                 */
1150                                 //dstream<<"Updating"<<std::endl;
1151                                 block->deSerialize(istr, ser_version);
1152                                 //block->setChangedFlag();
1153                         }
1154                         catch(InvalidPositionException &e)
1155                         {
1156                                 /*
1157                                         Create a new block
1158                                 */
1159                                 //dstream<<"Creating new"<<std::endl;
1160                                 block = new MapBlock(&m_env.getMap(), p);
1161                                 block->deSerialize(istr, ser_version);
1162                                 sector->insertBlock(block);
1163                                 //block->setChangedFlag();
1164
1165                                 //DEBUG
1166                                 /*NodeMod mod;
1167                                 mod.type = NODEMOD_CHANGECONTENT;
1168                                 mod.param = CONTENT_MESE;
1169                                 block->setTempMod(v3s16(8,10,8), mod);
1170                                 block->setTempMod(v3s16(8,9,8), mod);
1171                                 block->setTempMod(v3s16(8,8,8), mod);
1172                                 block->setTempMod(v3s16(8,7,8), mod);
1173                                 block->setTempMod(v3s16(8,6,8), mod);*/
1174                                 
1175                                 /*
1176                                         Add some coulds
1177                                         Well, this is a dumb way to do it, they should just
1178                                         be drawn as separate objects.
1179                                 */
1180                                 /*if(p.Y == 3)
1181                                 {
1182                                         NodeMod mod;
1183                                         mod.type = NODEMOD_CHANGECONTENT;
1184                                         mod.param = CONTENT_CLOUD;
1185                                         v3s16 p2;
1186                                         p2.Y = 8;
1187                                         for(p2.X=3; p2.X<=13; p2.X++)
1188                                         for(p2.Z=3; p2.Z<=13; p2.Z++)
1189                                         {
1190                                                 block->setTempMod(p2, mod);
1191                                         }
1192                                 }*/
1193                         }
1194                 } //envlock
1195                 
1196                 
1197                 // Old version has zero lighting, update it.
1198                 if(ser_version == 0 || ser_version == 1)
1199                 {
1200                         derr_client<<"Client: Block in old format: "
1201                                         "Calculating lighting"<<std::endl;
1202                         core::map<v3s16, MapBlock*> blocks_changed;
1203                         blocks_changed.insert(block->getPos(), block);
1204                         core::map<v3s16, MapBlock*> modified_blocks;
1205                         m_env.getMap().updateLighting(blocks_changed, modified_blocks);
1206                 }
1207
1208                 /*
1209                         Update Mesh of this block and blocks at x-, y- and z-
1210                 */
1211
1212                 //m_env.getMap().updateMeshes(block->getPos());
1213                 mesh_updater.add(block->getPos());
1214                 
1215                 /*
1216                         Acknowledge block.
1217                 */
1218                 /*
1219                         [0] u16 command
1220                         [2] u8 count
1221                         [3] v3s16 pos_0
1222                         [3+6] v3s16 pos_1
1223                         ...
1224                 */
1225                 u32 replysize = 2+1+6;
1226                 SharedBuffer<u8> reply(replysize);
1227                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1228                 reply[2] = 1;
1229                 writeV3S16(&reply[3], p);
1230                 // Send as reliable
1231                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1232
1233 #if 0
1234                 /*
1235                         Remove from history
1236                 */
1237                 {
1238                         JMutexAutoLock lock(m_fetchblock_mutex);
1239                         
1240                         if(m_fetchblock_history.find(p) != NULL)
1241                         {
1242                                 m_fetchblock_history.remove(p);
1243                         }
1244                         else
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                 }
1265 #endif
1266         }
1267         else
1268         {
1269                 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1270                                 <<command<<std::endl;
1271         }
1272
1273         return true;
1274
1275         } //try
1276         catch(con::PeerNotFoundException &e)
1277         {
1278                 dout_client<<DTIME<<"Client::AsyncProcessData(): Cancelling: The server"
1279                                 " connection doesn't exist (a timeout or not yet connected?)"<<std::endl;
1280                 return false;
1281         }
1282 }
1283
1284 bool Client::AsyncProcessData()
1285 {
1286         for(;;)
1287         {
1288                 // We want to update the meshes as soon as a single packet has
1289                 // been processed
1290                 LazyMeshUpdater mesh_updater(&m_env);
1291                 bool r = AsyncProcessPacket(mesh_updater);
1292                 if(r == false)
1293                         break;
1294         }
1295         return false;
1296
1297         /*LazyMeshUpdater mesh_updater(&m_env);
1298         for(;;)
1299         {
1300                 bool r = AsyncProcessPacket(mesh_updater);
1301                 if(r == false)
1302                         break;
1303         }
1304         return false;*/
1305
1306 }
1307
1308 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1309 {
1310         JMutexAutoLock lock(m_con_mutex);
1311         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1312 }
1313
1314 #if 0
1315 void Client::fetchBlock(v3s16 p, u8 flags)
1316 {
1317         if(connectedAndInitialized() == false)
1318                 throw ClientNotReadyException
1319                 ("ClientNotReadyException: connectedAndInitialized() == false");
1320
1321         /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
1322                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1323
1324         JMutexAutoLock conlock(m_con_mutex);
1325
1326         SharedBuffer<u8> data(9);
1327         writeU16(&data[0], TOSERVER_GETBLOCK);
1328         writeS16(&data[2], p.X);
1329         writeS16(&data[4], p.Y);
1330         writeS16(&data[6], p.Z);
1331         writeU8(&data[8], flags);
1332         m_con.Send(PEER_ID_SERVER, 1, data, true);
1333 }
1334
1335 /*
1336         Calls fetchBlock() on some nearby missing blocks.
1337
1338         Returns when any of various network load indicators go over limit.
1339
1340         Does nearly the same thing as the old updateChangedVisibleArea()
1341 */
1342 void Client::fetchBlocks()
1343 {
1344         if(connectedAndInitialized() == false)
1345                 throw ClientNotReadyException
1346                 ("ClientNotReadyException: connectedAndInitialized() == false");
1347 }
1348 #endif
1349
1350 bool Client::isFetchingBlocks()
1351 {
1352         JMutexAutoLock conlock(m_con_mutex);
1353         con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
1354         // Not really fetching but can't fetch more.
1355         if(peer == NULL) return true;
1356
1357         con::Channel *channel = &(peer->channels[1]);
1358         /*
1359                 NOTE: Channel 0 should always be used for fetching blocks,
1360                       and for nothing else.
1361         */
1362         if(channel->incoming_reliables.size() > 0)
1363                 return true;
1364         if(channel->outgoing_reliables.size() > 0)
1365                 return true;
1366         return false;
1367 }
1368
1369 IncomingPacket Client::getPacket()
1370 {
1371         JMutexAutoLock lock(m_incoming_queue_mutex);
1372         
1373         core::list<IncomingPacket>::Iterator i;
1374         // Refer to first one
1375         i = m_incoming_queue.begin();
1376
1377         // If queue is empty, return empty packet
1378         if(i == m_incoming_queue.end()){
1379                 IncomingPacket packet;
1380                 return packet;
1381         }
1382         
1383         // Pop out first packet and return it
1384         IncomingPacket packet = *i;
1385         m_incoming_queue.erase(i);
1386         return packet;
1387 }
1388
1389 #if 0
1390 void Client::removeNode(v3s16 nodepos)
1391 {
1392         if(connectedAndInitialized() == false){
1393                 dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
1394                                 <<std::endl;
1395                 return;
1396         }
1397         
1398         // Test that the position exists
1399         try{
1400                 JMutexAutoLock envlock(m_env_mutex);
1401                 m_env.getMap().getNode(nodepos);
1402         }
1403         catch(InvalidPositionException &e)
1404         {
1405                 dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
1406                                 <<std::endl;
1407                 return;
1408         }
1409
1410         SharedBuffer<u8> data(8);
1411         writeU16(&data[0], TOSERVER_REMOVENODE);
1412         writeS16(&data[2], nodepos.X);
1413         writeS16(&data[4], nodepos.Y);
1414         writeS16(&data[6], nodepos.Z);
1415         Send(0, data, true);
1416 }
1417
1418 void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
1419 {
1420         if(connectedAndInitialized() == false){
1421                 dout_client<<DTIME<<"Client::addNodeFromInventory() "
1422                                 "cancelled (not connected)"
1423                                 <<std::endl;
1424                 return;
1425         }
1426         
1427         // Test that the position exists
1428         try{
1429                 JMutexAutoLock envlock(m_env_mutex);
1430                 m_env.getMap().getNode(nodepos);
1431         }
1432         catch(InvalidPositionException &e)
1433         {
1434                 dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
1435                                 <<std::endl;
1436                 return;
1437         }
1438
1439         //u8 ser_version = m_server_ser_ver;
1440
1441         // SUGGESTION: The validity of the operation could be checked here too
1442
1443         u8 datasize = 2 + 6 + 2;
1444         SharedBuffer<u8> data(datasize);
1445         writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
1446         writeS16(&data[2], nodepos.X);
1447         writeS16(&data[4], nodepos.Y);
1448         writeS16(&data[6], nodepos.Z);
1449         writeU16(&data[8], i);
1450         Send(0, data, true);
1451 }
1452 #endif
1453
1454 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1455                 v3s16 nodepos_oversurface, u16 item)
1456 {
1457         if(connectedAndInitialized() == false){
1458                 dout_client<<DTIME<<"Client::groundAction() "
1459                                 "cancelled (not connected)"
1460                                 <<std::endl;
1461                 return;
1462         }
1463         
1464         /*
1465                 length: 17
1466                 [0] u16 command
1467                 [2] u8 action
1468                 [3] v3s16 nodepos_undersurface
1469                 [9] v3s16 nodepos_abovesurface
1470                 [15] u16 item
1471                 actions:
1472                 0: start digging
1473                 1: place block
1474                 2: stop digging (all parameters ignored)
1475         */
1476         u8 datasize = 2 + 1 + 6 + 6 + 2;
1477         SharedBuffer<u8> data(datasize);
1478         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1479         writeU8(&data[2], action);
1480         writeV3S16(&data[3], nodepos_undersurface);
1481         writeV3S16(&data[9], nodepos_oversurface);
1482         writeU16(&data[15], item);
1483         Send(0, data, true);
1484 }
1485
1486 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1487 {
1488         if(connectedAndInitialized() == false){
1489                 dout_client<<DTIME<<"Client::clickObject() "
1490                                 "cancelled (not connected)"
1491                                 <<std::endl;
1492                 return;
1493         }
1494         
1495         /*
1496                 [0] u16 command
1497                 [2] u8 button (0=left, 1=right)
1498                 [3] v3s16 block
1499                 [9] s16 id
1500                 [11] u16 item
1501         */
1502         u8 datasize = 2 + 1 + 6 + 2 + 2;
1503         SharedBuffer<u8> data(datasize);
1504         writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1505         writeU8(&data[2], button);
1506         writeV3S16(&data[3], blockpos);
1507         writeS16(&data[9], id);
1508         writeU16(&data[11], item);
1509         Send(0, data, true);
1510 }
1511
1512 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1513 {
1514         /*
1515                 u16 command
1516                 v3s16 blockpos
1517                 s16 id
1518                 u16 textlen
1519                 textdata
1520         */
1521         std::ostringstream os(std::ios_base::binary);
1522         u8 buf[12];
1523         
1524         // Write command
1525         writeU16(buf, TOSERVER_SIGNTEXT);
1526         os.write((char*)buf, 2);
1527         
1528         // Write blockpos
1529         writeV3S16(buf, blockpos);
1530         os.write((char*)buf, 6);
1531
1532         // Write id
1533         writeS16(buf, id);
1534         os.write((char*)buf, 2);
1535
1536         u16 textlen = text.size();
1537         // Write text length
1538         writeS16(buf, textlen);
1539         os.write((char*)buf, 2);
1540
1541         // Write text
1542         os.write((char*)text.c_str(), textlen);
1543         
1544         // Make data buffer
1545         std::string s = os.str();
1546         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1547         // Send as reliable
1548         Send(0, data, true);
1549 }
1550
1551 void Client::sendPlayerPos()
1552 {
1553         JMutexAutoLock envlock(m_env_mutex);
1554         
1555         Player *myplayer = m_env.getLocalPlayer();
1556         if(myplayer == NULL)
1557                 return;
1558         
1559         u16 our_peer_id;
1560         {
1561                 JMutexAutoLock lock(m_con_mutex);
1562                 our_peer_id = m_con.GetPeerID();
1563         }
1564         
1565         // Set peer id if not set already
1566         if(myplayer->peer_id == PEER_ID_NEW)
1567                 myplayer->peer_id = our_peer_id;
1568         // Check that an existing peer_id is the same as the connection's
1569         assert(myplayer->peer_id == our_peer_id);
1570         
1571         v3f pf = myplayer->getPosition();
1572         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1573         v3f sf = myplayer->getSpeed();
1574         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1575         s32 pitch = myplayer->getPitch() * 100;
1576         s32 yaw = myplayer->getYaw() * 100;
1577
1578         /*
1579                 Format:
1580                 [0] u16 command
1581                 [2] v3s32 position*100
1582                 [2+12] v3s32 speed*100
1583                 [2+12+12] s32 pitch*100
1584                 [2+12+12+4] s32 yaw*100
1585         */
1586
1587         SharedBuffer<u8> data(2+12+12+4+4);
1588         writeU16(&data[0], TOSERVER_PLAYERPOS);
1589         writeV3S32(&data[2], position);
1590         writeV3S32(&data[2+12], speed);
1591         writeS32(&data[2+12+12], pitch);
1592         writeS32(&data[2+12+12+4], yaw);
1593
1594         // Send as unreliable
1595         Send(0, data, false);
1596 }
1597
1598
1599 void Client::updateCamera(v3f pos, v3f dir)
1600 {
1601         m_env.getMap().updateCamera(pos, dir);
1602         camera_position = pos;
1603         camera_direction = dir;
1604 }
1605
1606 MapNode Client::getNode(v3s16 p)
1607 {
1608         JMutexAutoLock envlock(m_env_mutex);
1609         return m_env.getMap().getNode(p);
1610 }
1611
1612 /*void Client::getNode(v3s16 p, MapNode n)
1613 {
1614         JMutexAutoLock envlock(m_env_mutex);
1615         m_env.getMap().setNode(p, n);
1616 }*/
1617
1618 /*f32 Client::getGroundHeight(v2s16 p)
1619 {
1620         JMutexAutoLock envlock(m_env_mutex);
1621         return m_env.getMap().getGroundHeight(p);
1622 }*/
1623
1624 /*bool Client::isNodeUnderground(v3s16 p)
1625 {
1626         JMutexAutoLock envlock(m_env_mutex);
1627         return m_env.getMap().isNodeUnderground(p);
1628 }*/
1629
1630 /*Player * Client::getLocalPlayer()
1631 {
1632         JMutexAutoLock envlock(m_env_mutex);
1633         return m_env.getLocalPlayer();
1634 }*/
1635
1636 /*core::list<Player*> Client::getPlayers()
1637 {
1638         JMutexAutoLock envlock(m_env_mutex);
1639         return m_env.getPlayers();
1640 }*/
1641
1642 v3f Client::getPlayerPosition()
1643 {
1644         JMutexAutoLock envlock(m_env_mutex);
1645         LocalPlayer *player = m_env.getLocalPlayer();
1646         assert(player != NULL);
1647         return player->getPosition();
1648 }
1649
1650 void Client::setPlayerControl(PlayerControl &control)
1651 {
1652         JMutexAutoLock envlock(m_env_mutex);
1653         LocalPlayer *player = m_env.getLocalPlayer();
1654         assert(player != NULL);
1655         player->control = control;
1656 }
1657
1658 // Returns true if the inventory of the local player has been
1659 // updated from the server. If it is true, it is set to false.
1660 bool Client::getLocalInventoryUpdated()
1661 {
1662         // m_inventory_updated is behind envlock
1663         JMutexAutoLock envlock(m_env_mutex);
1664         bool updated = m_inventory_updated;
1665         m_inventory_updated = false;
1666         return updated;
1667 }
1668
1669 // Copies the inventory of the local player to parameter
1670 void Client::getLocalInventory(Inventory &dst)
1671 {
1672         JMutexAutoLock envlock(m_env_mutex);
1673         Player *player = m_env.getLocalPlayer();
1674         assert(player != NULL);
1675         dst = player->inventory;
1676 }
1677
1678 MapBlockObject * Client::getSelectedObject(
1679                 f32 max_d,
1680                 v3f from_pos_f_on_map,
1681                 core::line3d<f32> shootline_on_map
1682         )
1683 {
1684         JMutexAutoLock envlock(m_env_mutex);
1685
1686         core::array<DistanceSortedObject> objects;
1687
1688         for(core::map<v3s16, bool>::Iterator
1689                         i = m_active_blocks.getIterator();
1690                         i.atEnd() == false; i++)
1691         {
1692                 v3s16 p = i.getNode()->getKey();
1693
1694                 MapBlock *block = NULL;
1695                 try
1696                 {
1697                         block = m_env.getMap().getBlockNoCreate(p);
1698                 }
1699                 catch(InvalidPositionException &e)
1700                 {
1701                         continue;
1702                 }
1703
1704                 // Calculate from_pos relative to block
1705                 v3s16 block_pos_i_on_map = block->getPosRelative();
1706                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1707                 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1708
1709                 block->getObjects(from_pos_f_on_block, max_d, objects);
1710                 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1711         }
1712
1713         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1714         
1715         // Sort them.
1716         // After this, the closest object is the first in the array.
1717         objects.sort();
1718
1719         for(u32 i=0; i<objects.size(); i++)
1720         {
1721                 MapBlockObject *obj = objects[i].obj;
1722                 MapBlock *block = obj->getBlock();
1723
1724                 // Calculate shootline relative to block
1725                 v3s16 block_pos_i_on_map = block->getPosRelative();
1726                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map);
1727                 core::line3d<f32> shootline_on_block(
1728                                 shootline_on_map.start - block_pos_f_on_map,
1729                                 shootline_on_map.end - block_pos_f_on_map
1730                 );
1731
1732                 if(obj->isSelected(shootline_on_block))
1733                 {
1734                         //dstream<<"Returning selected object"<<std::endl;
1735                         return obj;
1736                 }
1737         }
1738
1739         //dstream<<"No object selected; returning NULL."<<std::endl;
1740         return NULL;
1741 }
1742
1743 void Client::printDebugInfo(std::ostream &os)
1744 {
1745         //JMutexAutoLock lock1(m_fetchblock_mutex);
1746         JMutexAutoLock lock2(m_incoming_queue_mutex);
1747
1748         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1749                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1750                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1751                 <<std::endl;
1752 }
1753         
1754 /*s32 Client::getDayNightIndex()
1755 {
1756         assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1757         return m_daynight_i;
1758 }*/
1759
1760 u32 Client::getDayNightRatio()
1761 {
1762         JMutexAutoLock envlock(m_env_mutex);
1763         return m_env.getDayNightRatio();
1764 }
1765
1766 /*void Client::updateSomeExpiredMeshes()
1767 {
1768         TimeTaker timer("updateSomeExpiredMeshes()", g_device);
1769         
1770         Player *player;
1771         {
1772                 JMutexAutoLock envlock(m_env_mutex);
1773                 player = m_env.getLocalPlayer();
1774         }
1775
1776         u32 daynight_ratio = getDayNightRatio();
1777
1778         v3f playerpos = player->getPosition();
1779         v3f playerspeed = player->getSpeed();
1780
1781         v3s16 center_nodepos = floatToInt(playerpos);
1782         v3s16 center = getNodeBlockPos(center_nodepos);
1783
1784         u32 counter = 0;
1785
1786         s16 d_max = 5;
1787         
1788         for(s16 d = 0; d <= d_max; d++)
1789         {
1790                 core::list<v3s16> list;
1791                 getFacePositions(list, d);
1792                 
1793                 core::list<v3s16>::Iterator li;
1794                 for(li=list.begin(); li!=list.end(); li++)
1795                 {
1796                         v3s16 p = *li + center;
1797                         MapBlock *block = NULL;
1798                         try
1799                         {
1800                                 //JMutexAutoLock envlock(m_env_mutex);
1801                                 block = m_env.getMap().getBlockNoCreate(p);
1802                         }
1803                         catch(InvalidPositionException &e)
1804                         {
1805                         }
1806
1807                         if(block == NULL)
1808                                 continue;
1809
1810                         if(block->getMeshExpired() == false)
1811                                 continue;
1812
1813                         block->updateMesh(daynight_ratio);
1814
1815                         counter++;
1816                         if(counter >= 5)
1817                                 return;
1818                 }
1819         }
1820 }*/
1821