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