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