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