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