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