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