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