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