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