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