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