]> git.lizzy.rs Git - minetest.git/blob - src/client.cpp
Modified the mesh update thread's sleep time to speed it up (it was way too high)
[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().deleteUnusedSectors
224                                         (delete_unused_sectors_timeout,
225                                         true, &deleted_blocks);*/
226                         
227                         // Delete whole sectors
228                         u32 num = m_env.getMap().deleteUnusedSectors
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                 addUpdateMeshTaskWithEdge(p, true);
800         }
801         else if(command == TOCLIENT_PLAYERPOS)
802         {
803                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
804                                 <<std::endl;
805                 /*u16 our_peer_id;
806                 {
807                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
808                         our_peer_id = m_con.GetPeerID();
809                 }
810                 // Cancel if we don't have a peer id
811                 if(our_peer_id == PEER_ID_INEXISTENT){
812                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
813                                         "we have no peer id"
814                                         <<std::endl;
815                         return;
816                 }*/
817
818                 { //envlock
819                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
820                         
821                         u32 player_size = 2+12+12+4+4;
822                                 
823                         u32 player_count = (datasize-2) / player_size;
824                         u32 start = 2;
825                         for(u32 i=0; i<player_count; i++)
826                         {
827                                 u16 peer_id = readU16(&data[start]);
828
829                                 Player *player = m_env.getPlayer(peer_id);
830
831                                 // Skip if player doesn't exist
832                                 if(player == NULL)
833                                 {
834                                         start += player_size;
835                                         continue;
836                                 }
837
838                                 // Skip if player is local player
839                                 if(player->isLocal())
840                                 {
841                                         start += player_size;
842                                         continue;
843                                 }
844
845                                 v3s32 ps = readV3S32(&data[start+2]);
846                                 v3s32 ss = readV3S32(&data[start+2+12]);
847                                 s32 pitch_i = readS32(&data[start+2+12+12]);
848                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
849                                 /*dstream<<"Client: got "
850                                                 <<"pitch_i="<<pitch_i
851                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
852                                 f32 pitch = (f32)pitch_i / 100.0;
853                                 f32 yaw = (f32)yaw_i / 100.0;
854                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
855                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
856                                 player->setPosition(position);
857                                 player->setSpeed(speed);
858                                 player->setPitch(pitch);
859                                 player->setYaw(yaw);
860
861                                 /*dstream<<"Client: player "<<peer_id
862                                                 <<" pitch="<<pitch
863                                                 <<" yaw="<<yaw<<std::endl;*/
864
865                                 start += player_size;
866                         }
867                 } //envlock
868         }
869         else if(command == TOCLIENT_PLAYERINFO)
870         {
871                 u16 our_peer_id;
872                 {
873                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
874                         our_peer_id = m_con.GetPeerID();
875                 }
876                 // Cancel if we don't have a peer id
877                 if(our_peer_id == PEER_ID_INEXISTENT){
878                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
879                                         "we have no peer id"
880                                         <<std::endl;
881                         return;
882                 }
883                 
884                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
885
886                 { //envlock
887                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
888                         
889                         u32 item_size = 2+PLAYERNAME_SIZE;
890                         u32 player_count = (datasize-2) / item_size;
891                         u32 start = 2;
892                         // peer_ids
893                         core::list<u16> players_alive;
894                         for(u32 i=0; i<player_count; i++)
895                         {
896                                 // Make sure the name ends in '\0'
897                                 data[start+2+20-1] = 0;
898
899                                 u16 peer_id = readU16(&data[start]);
900
901                                 players_alive.push_back(peer_id);
902                                 
903                                 /*dstream<<DTIME<<"peer_id="<<peer_id
904                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
905
906                                 // Don't update the info of the local player
907                                 if(peer_id == our_peer_id)
908                                 {
909                                         start += item_size;
910                                         continue;
911                                 }
912
913                                 Player *player = m_env.getPlayer(peer_id);
914
915                                 // Create a player if it doesn't exist
916                                 if(player == NULL)
917                                 {
918                                         player = new RemotePlayer(
919                                                         m_device->getSceneManager()->getRootSceneNode(),
920                                                         m_device,
921                                                         -1);
922                                         player->peer_id = peer_id;
923                                         m_env.addPlayer(player);
924                                         dout_client<<DTIME<<"Client: Adding new player "
925                                                         <<peer_id<<std::endl;
926                                 }
927                                 
928                                 player->updateName((char*)&data[start+2]);
929
930                                 start += item_size;
931                         }
932                         
933                         /*
934                                 Remove those players from the environment that
935                                 weren't listed by the server.
936                         */
937                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
938                         core::list<Player*> players = m_env.getPlayers();
939                         core::list<Player*>::Iterator ip;
940                         for(ip=players.begin(); ip!=players.end(); ip++)
941                         {
942                                 // Ingore local player
943                                 if((*ip)->isLocal())
944                                         continue;
945                                 
946                                 // Warn about a special case
947                                 if((*ip)->peer_id == 0)
948                                 {
949                                         dstream<<DTIME<<"WARNING: Client: Removing "
950                                                         "dead player with id=0"<<std::endl;
951                                 }
952
953                                 bool is_alive = false;
954                                 core::list<u16>::Iterator i;
955                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
956                                 {
957                                         if((*ip)->peer_id == *i)
958                                         {
959                                                 is_alive = true;
960                                                 break;
961                                         }
962                                 }
963                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
964                                                 <<" is_alive="<<is_alive<<std::endl;*/
965                                 if(is_alive)
966                                         continue;
967                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
968                                                 <<std::endl;
969                                 m_env.removePlayer((*ip)->peer_id);
970                         }
971                 } //envlock
972         }
973         else if(command == TOCLIENT_SECTORMETA)
974         {
975                 /*
976                         [0] u16 command
977                         [2] u8 sector count
978                         [3...] v2s16 pos + sector metadata
979                 */
980                 if(datasize < 3)
981                         return;
982
983                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
984
985                 { //envlock
986                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
987                         
988                         std::string datastring((char*)&data[2], datasize-2);
989                         std::istringstream is(datastring, std::ios_base::binary);
990
991                         u8 buf[4];
992
993                         is.read((char*)buf, 1);
994                         u16 sector_count = readU8(buf);
995                         
996                         //dstream<<"sector_count="<<sector_count<<std::endl;
997
998                         for(u16 i=0; i<sector_count; i++)
999                         {
1000                                 // Read position
1001                                 is.read((char*)buf, 4);
1002                                 v2s16 pos = readV2S16(buf);
1003                                 /*dstream<<"Client: deserializing sector at "
1004                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1005                                 // Create sector
1006                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1007                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1008                         }
1009                 } //envlock
1010         }
1011         else if(command == TOCLIENT_INVENTORY)
1012         {
1013                 if(datasize < 3)
1014                         return;
1015
1016                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1017
1018                 { //envlock
1019                         //TimeTaker t2("mutex locking", m_device);
1020                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1021                         //t2.stop();
1022                         
1023                         //TimeTaker t3("istringstream init", m_device);
1024                         std::string datastring((char*)&data[2], datasize-2);
1025                         std::istringstream is(datastring, std::ios_base::binary);
1026                         //t3.stop();
1027                         
1028                         //m_env.printPlayers(dstream);
1029
1030                         //TimeTaker t4("player get", m_device);
1031                         Player *player = m_env.getLocalPlayer();
1032                         assert(player != NULL);
1033                         //t4.stop();
1034
1035                         //TimeTaker t1("inventory.deSerialize()", m_device);
1036                         player->inventory.deSerialize(is);
1037                         //t1.stop();
1038
1039                         m_inventory_updated = true;
1040
1041                         //dstream<<"Client got player inventory:"<<std::endl;
1042                         //player->inventory.print(dstream);
1043                 }
1044         }
1045         //DEBUG
1046         else if(command == TOCLIENT_OBJECTDATA)
1047         //else if(0)
1048         {
1049                 // Strip command word and create a stringstream
1050                 std::string datastring((char*)&data[2], datasize-2);
1051                 std::istringstream is(datastring, std::ios_base::binary);
1052                 
1053                 { //envlock
1054                 
1055                 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1056
1057                 u8 buf[12];
1058
1059                 /*
1060                         Read players
1061                 */
1062
1063                 is.read((char*)buf, 2);
1064                 u16 playercount = readU16(buf);
1065                 
1066                 for(u16 i=0; i<playercount; i++)
1067                 {
1068                         is.read((char*)buf, 2);
1069                         u16 peer_id = readU16(buf);
1070                         is.read((char*)buf, 12);
1071                         v3s32 p_i = readV3S32(buf);
1072                         is.read((char*)buf, 12);
1073                         v3s32 s_i = readV3S32(buf);
1074                         is.read((char*)buf, 4);
1075                         s32 pitch_i = readS32(buf);
1076                         is.read((char*)buf, 4);
1077                         s32 yaw_i = readS32(buf);
1078                         
1079                         Player *player = m_env.getPlayer(peer_id);
1080
1081                         // Skip if player doesn't exist
1082                         if(player == NULL)
1083                         {
1084                                 continue;
1085                         }
1086
1087                         // Skip if player is local player
1088                         if(player->isLocal())
1089                         {
1090                                 continue;
1091                         }
1092         
1093                         f32 pitch = (f32)pitch_i / 100.0;
1094                         f32 yaw = (f32)yaw_i / 100.0;
1095                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1096                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1097                         
1098                         player->setPosition(position);
1099                         player->setSpeed(speed);
1100                         player->setPitch(pitch);
1101                         player->setYaw(yaw);
1102                 }
1103
1104                 /*
1105                         Read block objects
1106                 */
1107
1108                 // Read active block count
1109                 is.read((char*)buf, 2);
1110                 u16 blockcount = readU16(buf);
1111                 
1112                 // Initialize delete queue with all active blocks
1113                 core::map<v3s16, bool> abs_to_delete;
1114                 for(core::map<v3s16, bool>::Iterator
1115                                 i = m_active_blocks.getIterator();
1116                                 i.atEnd() == false; i++)
1117                 {
1118                         v3s16 p = i.getNode()->getKey();
1119                         /*dstream<<"adding "
1120                                         <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1121                                         <<" to abs_to_delete"
1122                                         <<std::endl;*/
1123                         abs_to_delete.insert(p, true);
1124                 }
1125
1126                 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1127                                 <<std::endl;*/
1128                 
1129                 for(u16 i=0; i<blockcount; i++)
1130                 {
1131                         // Read blockpos
1132                         is.read((char*)buf, 6);
1133                         v3s16 p = readV3S16(buf);
1134                         // Get block from somewhere
1135                         MapBlock *block = NULL;
1136                         try{
1137                                 block = m_env.getMap().getBlockNoCreate(p);
1138                         }
1139                         catch(InvalidPositionException &e)
1140                         {
1141                                 //TODO: Create a dummy block?
1142                         }
1143                         if(block == NULL)
1144                         {
1145                                 dstream<<"WARNING: "
1146                                                 <<"Could not get block at blockpos "
1147                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1148                                                 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1149                                                 <<"following block object data."
1150                                                 <<std::endl;
1151                                 return;
1152                         }
1153
1154                         /*dstream<<"Client updating objects for block "
1155                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1156                                         <<std::endl;*/
1157
1158                         // Insert to active block list
1159                         m_active_blocks.insert(p, true);
1160
1161                         // Remove from deletion queue
1162                         if(abs_to_delete.find(p) != NULL)
1163                                 abs_to_delete.remove(p);
1164
1165                         /*
1166                                 Update objects of block
1167                                 
1168                                 NOTE: Be sure this is done in the main thread.
1169                         */
1170                         block->updateObjects(is, m_server_ser_ver,
1171                                         m_device->getSceneManager(), m_env.getDayNightRatio());
1172                 }
1173                 
1174                 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1175                                 <<std::endl;*/
1176                 
1177                 // Delete objects of blocks in delete queue
1178                 for(core::map<v3s16, bool>::Iterator
1179                                 i = abs_to_delete.getIterator();
1180                                 i.atEnd() == false; i++)
1181                 {
1182                         v3s16 p = i.getNode()->getKey();
1183                         try
1184                         {
1185                                 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1186                                 
1187                                 // Clear objects
1188                                 block->clearObjects();
1189                                 // Remove from active blocks list
1190                                 m_active_blocks.remove(p);
1191                         }
1192                         catch(InvalidPositionException &e)
1193                         {
1194                                 dstream<<"WARNAING: Client: "
1195                                                 <<"Couldn't clear objects of active->inactive"
1196                                                 <<" block "
1197                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1198                                                 <<" because block was not found"
1199                                                 <<std::endl;
1200                                 // Ignore
1201                         }
1202                 }
1203
1204                 } //envlock
1205         }
1206         else if(command == TOCLIENT_TIME_OF_DAY)
1207         {
1208                 if(datasize < 4)
1209                         return;
1210                 
1211                 u16 time_of_day = readU16(&data[2]);
1212                 time_of_day = time_of_day % 24000;
1213                 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1214                 
1215                 /*
1216                         time_of_day:
1217                         0 = midnight
1218                         12000 = midday
1219                 */
1220                 {
1221                         m_env.setTimeOfDay(time_of_day);
1222
1223                         u32 dr = m_env.getDayNightRatio();
1224
1225                         dstream<<"Client: time_of_day="<<time_of_day
1226                                         <<", dr="<<dr
1227                                         <<std::endl;
1228                 }
1229
1230         }
1231         else if(command == TOCLIENT_CHAT_MESSAGE)
1232         {
1233                 /*
1234                         u16 command
1235                         u16 length
1236                         wstring message
1237                 */
1238                 u8 buf[6];
1239                 std::string datastring((char*)&data[2], datasize-2);
1240                 std::istringstream is(datastring, std::ios_base::binary);
1241                 
1242                 // Read stuff
1243                 is.read((char*)buf, 2);
1244                 u16 len = readU16(buf);
1245                 
1246                 std::wstring message;
1247                 for(u16 i=0; i<len; i++)
1248                 {
1249                         is.read((char*)buf, 2);
1250                         message += (wchar_t)readU16(buf);
1251                 }
1252
1253                 /*dstream<<"Client received chat message: "
1254                                 <<wide_to_narrow(message)<<std::endl;*/
1255                 
1256                 m_chat_queue.push_back(message);
1257         }
1258         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1259         {
1260                 //if(g_settings.getBool("enable_experimental"))
1261                 {
1262                         /*
1263                                 u16 command
1264                                 u16 count of removed objects
1265                                 for all removed objects {
1266                                         u16 id
1267                                 }
1268                                 u16 count of added objects
1269                                 for all added objects {
1270                                         u16 id
1271                                         u8 type
1272                                         u16 initialization data length
1273                                         string initialization data
1274                                 }
1275                         */
1276
1277                         char buf[6];
1278                         // Get all data except the command number
1279                         std::string datastring((char*)&data[2], datasize-2);
1280                         // Throw them in an istringstream
1281                         std::istringstream is(datastring, std::ios_base::binary);
1282
1283                         // Read stuff
1284                         
1285                         // Read removed objects
1286                         is.read(buf, 2);
1287                         u16 removed_count = readU16((u8*)buf);
1288                         for(u16 i=0; i<removed_count; i++)
1289                         {
1290                                 is.read(buf, 2);
1291                                 u16 id = readU16((u8*)buf);
1292                                 // Remove it
1293                                 {
1294                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1295                                         m_env.removeActiveObject(id);
1296                                 }
1297                         }
1298                         
1299                         // Read added objects
1300                         is.read(buf, 2);
1301                         u16 added_count = readU16((u8*)buf);
1302                         for(u16 i=0; i<added_count; i++)
1303                         {
1304                                 is.read(buf, 2);
1305                                 u16 id = readU16((u8*)buf);
1306                                 is.read(buf, 1);
1307                                 u8 type = readU8((u8*)buf);
1308                                 std::string data = deSerializeLongString(is);
1309                                 // Add it
1310                                 {
1311                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1312                                         m_env.addActiveObject(id, type, data);
1313                                 }
1314                         }
1315                 }
1316         }
1317         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1318         {
1319                 //if(g_settings.getBool("enable_experimental"))
1320                 {
1321                         /*
1322                                 u16 command
1323                                 for all objects
1324                                 {
1325                                         u16 id
1326                                         u16 message length
1327                                         string message
1328                                 }
1329                         */
1330                         char buf[6];
1331                         // Get all data except the command number
1332                         std::string datastring((char*)&data[2], datasize-2);
1333                         // Throw them in an istringstream
1334                         std::istringstream is(datastring, std::ios_base::binary);
1335                         
1336                         while(is.eof() == false)
1337                         {
1338                                 // Read stuff
1339                                 is.read(buf, 2);
1340                                 u16 id = readU16((u8*)buf);
1341                                 if(is.eof())
1342                                         break;
1343                                 is.read(buf, 2);
1344                                 u16 message_size = readU16((u8*)buf);
1345                                 std::string message;
1346                                 message.reserve(message_size);
1347                                 for(u16 i=0; i<message_size; i++)
1348                                 {
1349                                         is.read(buf, 1);
1350                                         message.append(buf, 1);
1351                                 }
1352                                 // Pass on to the environment
1353                                 {
1354                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1355                                         m_env.processActiveObjectMessage(id, message);
1356                                 }
1357                         }
1358                 }
1359         }
1360         else if(command == TOCLIENT_HP)
1361         {
1362                 std::string datastring((char*)&data[2], datasize-2);
1363                 std::istringstream is(datastring, std::ios_base::binary);
1364                 Player *player = m_env.getLocalPlayer();
1365                 assert(player != NULL);
1366                 u8 hp = readU8(is);
1367                 player->hp = hp;
1368         }
1369         else if(command == TOCLIENT_MOVE_PLAYER)
1370         {
1371                 std::string datastring((char*)&data[2], datasize-2);
1372                 std::istringstream is(datastring, std::ios_base::binary);
1373                 Player *player = m_env.getLocalPlayer();
1374                 assert(player != NULL);
1375                 v3f pos = readV3F1000(is);
1376                 f32 pitch = readF1000(is);
1377                 f32 yaw = readF1000(is);
1378                 player->setPosition(pos);
1379                 /*player->setPitch(pitch);
1380                 player->setYaw(yaw);*/
1381
1382                 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1383                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1384                                 <<" pitch="<<pitch
1385                                 <<" yaw="<<yaw
1386                                 <<std::endl;
1387
1388                 /*
1389                         Add to ClientEvent queue.
1390                         This has to be sent to the main program because otherwise
1391                         it would just force the pitch and yaw values to whatever
1392                         the camera points to.
1393                 */
1394                 ClientEvent event;
1395                 event.type = CE_PLAYER_FORCE_MOVE;
1396                 event.player_force_move.pitch = pitch;
1397                 event.player_force_move.yaw = yaw;
1398                 m_client_event_queue.push_back(event);
1399
1400                 // Ignore damage for a few seconds, so that the player doesn't
1401                 // get damage from falling on ground
1402                 m_ignore_damage_timer = 3.0;
1403         }
1404         else
1405         {
1406                 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1407                                 <<command<<std::endl;
1408         }
1409 }
1410
1411 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1412 {
1413         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1414         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1415 }
1416
1417 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1418                 v3s16 nodepos_oversurface, u16 item)
1419 {
1420         if(connectedAndInitialized() == false){
1421                 dout_client<<DTIME<<"Client::groundAction() "
1422                                 "cancelled (not connected)"
1423                                 <<std::endl;
1424                 return;
1425         }
1426         
1427         /*
1428                 length: 17
1429                 [0] u16 command
1430                 [2] u8 action
1431                 [3] v3s16 nodepos_undersurface
1432                 [9] v3s16 nodepos_abovesurface
1433                 [15] u16 item
1434                 actions:
1435                 0: start digging
1436                 1: place block
1437                 2: stop digging (all parameters ignored)
1438                 3: digging completed
1439         */
1440         u8 datasize = 2 + 1 + 6 + 6 + 2;
1441         SharedBuffer<u8> data(datasize);
1442         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1443         writeU8(&data[2], action);
1444         writeV3S16(&data[3], nodepos_undersurface);
1445         writeV3S16(&data[9], nodepos_oversurface);
1446         writeU16(&data[15], item);
1447         Send(0, data, true);
1448 }
1449
1450 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1451 {
1452         if(connectedAndInitialized() == false){
1453                 dout_client<<DTIME<<"Client::clickObject() "
1454                                 "cancelled (not connected)"
1455                                 <<std::endl;
1456                 return;
1457         }
1458         
1459         /*
1460                 [0] u16 command=TOSERVER_CLICK_OBJECT
1461                 [2] u8 button (0=left, 1=right)
1462                 [3] v3s16 block
1463                 [9] s16 id
1464                 [11] u16 item
1465         */
1466         u8 datasize = 2 + 1 + 6 + 2 + 2;
1467         SharedBuffer<u8> data(datasize);
1468         writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1469         writeU8(&data[2], button);
1470         writeV3S16(&data[3], blockpos);
1471         writeS16(&data[9], id);
1472         writeU16(&data[11], item);
1473         Send(0, data, true);
1474 }
1475
1476 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1477 {
1478         if(connectedAndInitialized() == false){
1479                 dout_client<<DTIME<<"Client::clickActiveObject() "
1480                                 "cancelled (not connected)"
1481                                 <<std::endl;
1482                 return;
1483         }
1484         
1485         /*
1486                 length: 7
1487                 [0] u16 command
1488                 [2] u8 button (0=left, 1=right)
1489                 [3] u16 id
1490                 [5] u16 item
1491         */
1492         u8 datasize = 2 + 1 + 6 + 2 + 2;
1493         SharedBuffer<u8> data(datasize);
1494         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1495         writeU8(&data[2], button);
1496         writeU16(&data[3], id);
1497         writeU16(&data[5], item);
1498         Send(0, data, true);
1499 }
1500
1501 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1502 {
1503         /*
1504                 u16 command
1505                 v3s16 blockpos
1506                 s16 id
1507                 u16 textlen
1508                 textdata
1509         */
1510         std::ostringstream os(std::ios_base::binary);
1511         u8 buf[12];
1512         
1513         // Write command
1514         writeU16(buf, TOSERVER_SIGNTEXT);
1515         os.write((char*)buf, 2);
1516         
1517         // Write blockpos
1518         writeV3S16(buf, blockpos);
1519         os.write((char*)buf, 6);
1520
1521         // Write id
1522         writeS16(buf, id);
1523         os.write((char*)buf, 2);
1524
1525         u16 textlen = text.size();
1526         // Write text length
1527         writeS16(buf, textlen);
1528         os.write((char*)buf, 2);
1529
1530         // Write text
1531         os.write((char*)text.c_str(), textlen);
1532         
1533         // Make data buffer
1534         std::string s = os.str();
1535         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1536         // Send as reliable
1537         Send(0, data, true);
1538 }
1539         
1540 void Client::sendSignNodeText(v3s16 p, std::string text)
1541 {
1542         /*
1543                 u16 command
1544                 v3s16 p
1545                 u16 textlen
1546                 textdata
1547         */
1548         std::ostringstream os(std::ios_base::binary);
1549         u8 buf[12];
1550         
1551         // Write command
1552         writeU16(buf, TOSERVER_SIGNNODETEXT);
1553         os.write((char*)buf, 2);
1554         
1555         // Write p
1556         writeV3S16(buf, p);
1557         os.write((char*)buf, 6);
1558
1559         u16 textlen = text.size();
1560         // Write text length
1561         writeS16(buf, textlen);
1562         os.write((char*)buf, 2);
1563
1564         // Write text
1565         os.write((char*)text.c_str(), textlen);
1566         
1567         // Make data buffer
1568         std::string s = os.str();
1569         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1570         // Send as reliable
1571         Send(0, data, true);
1572 }
1573         
1574 void Client::sendInventoryAction(InventoryAction *a)
1575 {
1576         std::ostringstream os(std::ios_base::binary);
1577         u8 buf[12];
1578         
1579         // Write command
1580         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1581         os.write((char*)buf, 2);
1582
1583         a->serialize(os);
1584         
1585         // Make data buffer
1586         std::string s = os.str();
1587         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1588         // Send as reliable
1589         Send(0, data, true);
1590 }
1591
1592 void Client::sendChatMessage(const std::wstring &message)
1593 {
1594         std::ostringstream os(std::ios_base::binary);
1595         u8 buf[12];
1596         
1597         // Write command
1598         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1599         os.write((char*)buf, 2);
1600         
1601         // Write length
1602         writeU16(buf, message.size());
1603         os.write((char*)buf, 2);
1604         
1605         // Write string
1606         for(u32 i=0; i<message.size(); i++)
1607         {
1608                 u16 w = message[i];
1609                 writeU16(buf, w);
1610                 os.write((char*)buf, 2);
1611         }
1612         
1613         // Make data buffer
1614         std::string s = os.str();
1615         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1616         // Send as reliable
1617         Send(0, data, true);
1618 }
1619
1620 void Client::sendChangePassword(const std::wstring oldpassword,
1621                 const std::wstring newpassword)
1622 {
1623         Player *player = m_env.getLocalPlayer();
1624         if(player == NULL)
1625                 return;
1626
1627         std::string playername = player->getName();
1628         std::string oldpwd = translatePassword(playername, oldpassword);
1629         std::string newpwd = translatePassword(playername, newpassword);
1630
1631         std::ostringstream os(std::ios_base::binary);
1632         u8 buf[2+PASSWORD_SIZE*2];
1633         /*
1634                 [0] u16 TOSERVER_PASSWORD
1635                 [2] u8[28] old password
1636                 [30] u8[28] new password
1637         */
1638
1639         writeU16(buf, TOSERVER_PASSWORD);
1640         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1641         {
1642                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1643                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1644         }
1645         buf[2+PASSWORD_SIZE-1] = 0;
1646         buf[30+PASSWORD_SIZE-1] = 0;
1647         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1648
1649         // Make data buffer
1650         std::string s = os.str();
1651         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1652         // Send as reliable
1653         Send(0, data, true);
1654 }
1655
1656
1657 void Client::sendDamage(u8 damage)
1658 {
1659         DSTACK(__FUNCTION_NAME);
1660         std::ostringstream os(std::ios_base::binary);
1661
1662         writeU16(os, TOSERVER_DAMAGE);
1663         writeU8(os, damage);
1664
1665         // Make data buffer
1666         std::string s = os.str();
1667         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1668         // Send as reliable
1669         Send(0, data, true);
1670 }
1671
1672 void Client::sendPlayerPos()
1673 {
1674         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1675         
1676         Player *myplayer = m_env.getLocalPlayer();
1677         if(myplayer == NULL)
1678                 return;
1679         
1680         u16 our_peer_id;
1681         {
1682                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1683                 our_peer_id = m_con.GetPeerID();
1684         }
1685         
1686         // Set peer id if not set already
1687         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1688                 myplayer->peer_id = our_peer_id;
1689         // Check that an existing peer_id is the same as the connection's
1690         assert(myplayer->peer_id == our_peer_id);
1691         
1692         v3f pf = myplayer->getPosition();
1693         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1694         v3f sf = myplayer->getSpeed();
1695         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1696         s32 pitch = myplayer->getPitch() * 100;
1697         s32 yaw = myplayer->getYaw() * 100;
1698
1699         /*
1700                 Format:
1701                 [0] u16 command
1702                 [2] v3s32 position*100
1703                 [2+12] v3s32 speed*100
1704                 [2+12+12] s32 pitch*100
1705                 [2+12+12+4] s32 yaw*100
1706         */
1707
1708         SharedBuffer<u8> data(2+12+12+4+4);
1709         writeU16(&data[0], TOSERVER_PLAYERPOS);
1710         writeV3S32(&data[2], position);
1711         writeV3S32(&data[2+12], speed);
1712         writeS32(&data[2+12+12], pitch);
1713         writeS32(&data[2+12+12+4], yaw);
1714
1715         // Send as unreliable
1716         Send(0, data, false);
1717 }
1718
1719 void Client::removeNode(v3s16 p)
1720 {
1721         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1722         
1723         core::map<v3s16, MapBlock*> modified_blocks;
1724
1725         try
1726         {
1727                 //TimeTaker t("removeNodeAndUpdate", m_device);
1728                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1729         }
1730         catch(InvalidPositionException &e)
1731         {
1732         }
1733         
1734         for(core::map<v3s16, MapBlock * >::Iterator
1735                         i = modified_blocks.getIterator();
1736                         i.atEnd() == false; i++)
1737         {
1738                 v3s16 p = i.getNode()->getKey();
1739                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1740                 addUpdateMeshTaskWithEdge(p);
1741         }
1742 }
1743
1744 void Client::addNode(v3s16 p, MapNode n)
1745 {
1746         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1747
1748         TimeTaker timer1("Client::addNode()");
1749
1750         core::map<v3s16, MapBlock*> modified_blocks;
1751
1752         try
1753         {
1754                 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1755                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1756         }
1757         catch(InvalidPositionException &e)
1758         {}
1759         
1760         //TimeTaker timer2("Client::addNode(): updateMeshes");
1761
1762         for(core::map<v3s16, MapBlock * >::Iterator
1763                         i = modified_blocks.getIterator();
1764                         i.atEnd() == false; i++)
1765         {
1766                 v3s16 p = i.getNode()->getKey();
1767                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1768                 addUpdateMeshTaskWithEdge(p);
1769         }
1770 }
1771         
1772 void Client::updateCamera(v3f pos, v3f dir)
1773 {
1774         m_env.getClientMap().updateCamera(pos, dir);
1775         camera_position = pos;
1776         camera_direction = dir;
1777 }
1778
1779 MapNode Client::getNode(v3s16 p)
1780 {
1781         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1782         return m_env.getMap().getNode(p);
1783 }
1784
1785 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1786 {
1787         return m_env.getMap().getNodeMetadata(p);
1788 }
1789
1790 v3f Client::getPlayerPosition()
1791 {
1792         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1793         LocalPlayer *player = m_env.getLocalPlayer();
1794         assert(player != NULL);
1795         return player->getPosition();
1796 }
1797
1798 void Client::setPlayerControl(PlayerControl &control)
1799 {
1800         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1801         LocalPlayer *player = m_env.getLocalPlayer();
1802         assert(player != NULL);
1803         player->control = control;
1804 }
1805
1806 // Returns true if the inventory of the local player has been
1807 // updated from the server. If it is true, it is set to false.
1808 bool Client::getLocalInventoryUpdated()
1809 {
1810         // m_inventory_updated is behind envlock
1811         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1812         bool updated = m_inventory_updated;
1813         m_inventory_updated = false;
1814         return updated;
1815 }
1816
1817 // Copies the inventory of the local player to parameter
1818 void Client::getLocalInventory(Inventory &dst)
1819 {
1820         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1821         Player *player = m_env.getLocalPlayer();
1822         assert(player != NULL);
1823         dst = player->inventory;
1824 }
1825
1826 InventoryContext *Client::getInventoryContext()
1827 {
1828         return &m_inventory_context;
1829 }
1830
1831 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1832 {
1833         if(id == "current_player")
1834         {
1835                 assert(c->current_player);
1836                 return &(c->current_player->inventory);
1837         }
1838         
1839         Strfnd fn(id);
1840         std::string id0 = fn.next(":");
1841
1842         if(id0 == "nodemeta")
1843         {
1844                 v3s16 p;
1845                 p.X = stoi(fn.next(","));
1846                 p.Y = stoi(fn.next(","));
1847                 p.Z = stoi(fn.next(","));
1848                 NodeMetadata* meta = getNodeMetadata(p);
1849                 if(meta)
1850                         return meta->getInventory();
1851                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1852                                 <<"no metadata found"<<std::endl;
1853                 return NULL;
1854         }
1855
1856         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1857         return NULL;
1858 }
1859 void Client::inventoryAction(InventoryAction *a)
1860 {
1861         sendInventoryAction(a);
1862 }
1863
1864 MapBlockObject * Client::getSelectedObject(
1865                 f32 max_d,
1866                 v3f from_pos_f_on_map,
1867                 core::line3d<f32> shootline_on_map
1868         )
1869 {
1870         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1871
1872         core::array<DistanceSortedObject> objects;
1873
1874         for(core::map<v3s16, bool>::Iterator
1875                         i = m_active_blocks.getIterator();
1876                         i.atEnd() == false; i++)
1877         {
1878                 v3s16 p = i.getNode()->getKey();
1879
1880                 MapBlock *block = NULL;
1881                 try
1882                 {
1883                         block = m_env.getMap().getBlockNoCreate(p);
1884                 }
1885                 catch(InvalidPositionException &e)
1886                 {
1887                         continue;
1888                 }
1889
1890                 // Calculate from_pos relative to block
1891                 v3s16 block_pos_i_on_map = block->getPosRelative();
1892                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1893                 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1894
1895                 block->getObjects(from_pos_f_on_block, max_d, objects);
1896                 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1897         }
1898
1899         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1900         
1901         // Sort them.
1902         // After this, the closest object is the first in the array.
1903         objects.sort();
1904
1905         for(u32 i=0; i<objects.size(); i++)
1906         {
1907                 MapBlockObject *obj = objects[i].obj;
1908                 MapBlock *block = obj->getBlock();
1909
1910                 // Calculate shootline relative to block
1911                 v3s16 block_pos_i_on_map = block->getPosRelative();
1912                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1913                 core::line3d<f32> shootline_on_block(
1914                                 shootline_on_map.start - block_pos_f_on_map,
1915                                 shootline_on_map.end - block_pos_f_on_map
1916                 );
1917
1918                 if(obj->isSelected(shootline_on_block))
1919                 {
1920                         //dstream<<"Returning selected object"<<std::endl;
1921                         return obj;
1922                 }
1923         }
1924
1925         //dstream<<"No object selected; returning NULL."<<std::endl;
1926         return NULL;
1927 }
1928
1929 ClientActiveObject * Client::getSelectedActiveObject(
1930                 f32 max_d,
1931                 v3f from_pos_f_on_map,
1932                 core::line3d<f32> shootline_on_map
1933         )
1934 {
1935         core::array<DistanceSortedActiveObject> objects;
1936
1937         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1938
1939         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1940         
1941         // Sort them.
1942         // After this, the closest object is the first in the array.
1943         objects.sort();
1944
1945         for(u32 i=0; i<objects.size(); i++)
1946         {
1947                 ClientActiveObject *obj = objects[i].obj;
1948                 
1949                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1950                 if(selection_box == NULL)
1951                         continue;
1952
1953                 v3f pos = obj->getPosition();
1954
1955                 core::aabbox3d<f32> offsetted_box(
1956                                 selection_box->MinEdge + pos,
1957                                 selection_box->MaxEdge + pos
1958                 );
1959
1960                 if(offsetted_box.intersectsWithLine(shootline_on_map))
1961                 {
1962                         //dstream<<"Returning selected object"<<std::endl;
1963                         return obj;
1964                 }
1965         }
1966
1967         //dstream<<"No object selected; returning NULL."<<std::endl;
1968         return NULL;
1969 }
1970
1971 void Client::printDebugInfo(std::ostream &os)
1972 {
1973         //JMutexAutoLock lock1(m_fetchblock_mutex);
1974         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1975
1976         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1977                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1978                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1979                 <<std::endl;*/
1980 }
1981         
1982 /*s32 Client::getDayNightIndex()
1983 {
1984         assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1985         return m_daynight_i;
1986 }*/
1987
1988 u32 Client::getDayNightRatio()
1989 {
1990         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1991         return m_env.getDayNightRatio();
1992 }
1993
1994 u16 Client::getHP()
1995 {
1996         Player *player = m_env.getLocalPlayer();
1997         assert(player != NULL);
1998         return player->hp;
1999 }
2000
2001 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2002 {
2003         /*dstream<<"Client::addUpdateMeshTask(): "
2004                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2005                         <<std::endl;*/
2006
2007         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2008         if(b == NULL)
2009                 return;
2010
2011         /*
2012                 Create a task to update the mesh of the block
2013         */
2014         
2015         MeshMakeData *data = new MeshMakeData;
2016         
2017         {
2018                 //TimeTaker timer("data fill");
2019                 // 0ms
2020                 data->fill(getDayNightRatio(), b);
2021         }
2022
2023         // Debug wait
2024         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2025         
2026         // Add task to queue
2027         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2028
2029         /*dstream<<"Mesh update input queue size is "
2030                         <<m_mesh_update_thread.m_queue_in.size()
2031                         <<std::endl;*/
2032         
2033 #if 0
2034         // Temporary test: make mesh directly in here
2035         {
2036                 //TimeTaker timer("make mesh");
2037                 // 10ms
2038                 scene::SMesh *mesh_new = NULL;
2039                 mesh_new = makeMapBlockMesh(data);
2040                 b->replaceMesh(mesh_new);
2041                 delete data;
2042         }
2043 #endif
2044
2045         b->setMeshExpired(false);
2046 }
2047
2048 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2049 {
2050         /*{
2051                 v3s16 p = blockpos;
2052                 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2053                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2054                                 <<std::endl;
2055         }*/
2056
2057         try{
2058                 v3s16 p = blockpos + v3s16(0,0,0);
2059                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2060                 addUpdateMeshTask(p, ack_to_server);
2061         }
2062         catch(InvalidPositionException &e){}
2063         // Leading edge
2064         try{
2065                 v3s16 p = blockpos + v3s16(-1,0,0);
2066                 addUpdateMeshTask(p);
2067         }
2068         catch(InvalidPositionException &e){}
2069         try{
2070                 v3s16 p = blockpos + v3s16(0,-1,0);
2071                 addUpdateMeshTask(p);
2072         }
2073         catch(InvalidPositionException &e){}
2074         try{
2075                 v3s16 p = blockpos + v3s16(0,0,-1);
2076                 addUpdateMeshTask(p);
2077         }
2078         catch(InvalidPositionException &e){}
2079 }
2080
2081 ClientEvent Client::getClientEvent()
2082 {
2083         if(m_client_event_queue.size() == 0)
2084         {
2085                 ClientEvent event;
2086                 event.type = CE_NONE;
2087                 return event;
2088         }
2089         return m_client_event_queue.pop_front();
2090 }
2091
2092