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