]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client.cpp
fix to the previous commit
[dragonfireclient.git] / src / client.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "jmutexautolock.h"
25 #include "main.h"
26 #include <sstream>
27 #include "porting.h"
28
29 void * MeshUpdateThread::Thread()
30 {
31         ThreadStarted();
32
33         DSTACK(__FUNCTION_NAME);
34         
35         BEGIN_DEBUG_EXCEPTION_HANDLER
36
37         while(getRun())
38         {
39                 QueuedMeshUpdate *q = m_queue_in.pop();
40                 if(q == NULL)
41                 {
42                         sleep_ms(50);
43                         continue;
44                 }
45
46                 scene::SMesh *mesh_new = NULL;
47                 mesh_new = makeMapBlockMesh(q->data);
48
49                 MeshUpdateResult r;
50                 r.p = q->p;
51                 r.mesh = mesh_new;
52                 r.ack_block_to_server = q->ack_block_to_server;
53
54                 /*dstream<<"MeshUpdateThread: Processed "
55                                 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
56                                 <<std::endl;*/
57
58                 m_queue_out.push_back(r);
59
60                 delete q;
61         }
62
63         END_DEBUG_EXCEPTION_HANDLER
64
65         return NULL;
66 }
67
68 Client::Client(
69                 IrrlichtDevice *device,
70                 const char *playername,
71                 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         // Execute fast commands straight away
550
551         if(command == TOCLIENT_INIT)
552         {
553                 if(datasize < 3)
554                         return;
555
556                 u8 deployed = data[2];
557
558                 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
559                                 "deployed="<<((int)deployed&0xff)<<std::endl;
560
561                 if(deployed < SER_FMT_VER_LOWEST
562                                 || deployed > SER_FMT_VER_HIGHEST)
563                 {
564                         derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
565                                         <<"unsupported ser_fmt_ver"<<std::endl;
566                         return;
567                 }
568                 
569                 m_server_ser_ver = deployed;
570
571                 // Get player position
572                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
573                 if(datasize >= 2+1+6)
574                         playerpos_s16 = readV3S16(&data[2+1]);
575                 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
576
577                 { //envlock
578                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
579                         
580                         // Set player position
581                         Player *player = m_env.getLocalPlayer();
582                         assert(player != NULL);
583                         player->setPosition(playerpos_f);
584                 }
585                 
586                 if(datasize >= 2+1+6+8)
587                 {
588                         // Get map seed
589                         m_map_seed = readU64(&data[2+1+6]);
590                         dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
591                 }
592                 
593                 // Reply to server
594                 u32 replysize = 2;
595                 SharedBuffer<u8> reply(replysize);
596                 writeU16(&reply[0], TOSERVER_INIT2);
597                 // Send as reliable
598                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
599
600                 return;
601         }
602         
603         if(ser_version == SER_FMT_VER_INVALID)
604         {
605                 dout_client<<DTIME<<"WARNING: Client: Server serialization"
606                                 " format invalid or not initialized."
607                                 " Skipping incoming command="<<command<<std::endl;
608                 return;
609         }
610         
611         // Just here to avoid putting the two if's together when
612         // making some copypasta
613         {}
614
615         if(command == TOCLIENT_REMOVENODE)
616         {
617                 if(datasize < 8)
618                         return;
619                 v3s16 p;
620                 p.X = readS16(&data[2]);
621                 p.Y = readS16(&data[4]);
622                 p.Z = readS16(&data[6]);
623                 
624                 //TimeTaker t1("TOCLIENT_REMOVENODE");
625                 
626                 // This will clear the cracking animation after digging
627                 ((ClientMap&)m_env.getMap()).clearTempMod(p);
628
629                 removeNode(p);
630         }
631         else if(command == TOCLIENT_ADDNODE)
632         {
633                 if(datasize < 8 + MapNode::serializedLength(ser_version))
634                         return;
635
636                 v3s16 p;
637                 p.X = readS16(&data[2]);
638                 p.Y = readS16(&data[4]);
639                 p.Z = readS16(&data[6]);
640                 
641                 //TimeTaker t1("TOCLIENT_ADDNODE");
642
643                 MapNode n;
644                 n.deSerialize(&data[8], ser_version);
645                 
646                 addNode(p, n);
647         }
648         else if(command == TOCLIENT_BLOCKDATA)
649         {
650                 // Ignore too small packet
651                 if(datasize < 8)
652                         return;
653                         
654                 v3s16 p;
655                 p.X = readS16(&data[2]);
656                 p.Y = readS16(&data[4]);
657                 p.Z = readS16(&data[6]);
658                 
659                 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
660                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
661                 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
662                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
663                 
664                 std::string datastring((char*)&data[8], datasize-8);
665                 std::istringstream istr(datastring, std::ios_base::binary);
666                 
667                 MapSector *sector;
668                 MapBlock *block;
669                 
670                 { //envlock
671                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
672                         
673                         v2s16 p2d(p.X, p.Z);
674                         sector = m_env.getMap().emergeSector(p2d);
675                         
676                         v2s16 sp = sector->getPos();
677                         if(sp != p2d)
678                         {
679                                 dstream<<"ERROR: Got sector with getPos()="
680                                                 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
681                                                 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
682                         }
683
684                         assert(sp == p2d);
685                         //assert(sector->getPos() == p2d);
686
687                         //TimeTaker timer("MapBlock deSerialize");
688                         // 0ms
689                         
690                         try{
691                                 block = sector->getBlockNoCreate(p.Y);
692                                 /*
693                                         Update an existing block
694                                 */
695                                 //dstream<<"Updating"<<std::endl;
696                                 block->deSerialize(istr, ser_version);
697                                 //block->setChangedFlag();
698                         }
699                         catch(InvalidPositionException &e)
700                         {
701                                 /*
702                                         Create a new block
703                                 */
704                                 //dstream<<"Creating new"<<std::endl;
705                                 block = new MapBlock(&m_env.getMap(), p);
706                                 block->deSerialize(istr, ser_version);
707                                 sector->insertBlock(block);
708                                 //block->setChangedFlag();
709
710                                 //DEBUG
711                                 /*NodeMod mod;
712                                 mod.type = NODEMOD_CHANGECONTENT;
713                                 mod.param = CONTENT_MESE;
714                                 block->setTempMod(v3s16(8,10,8), mod);
715                                 block->setTempMod(v3s16(8,9,8), mod);
716                                 block->setTempMod(v3s16(8,8,8), mod);
717                                 block->setTempMod(v3s16(8,7,8), mod);
718                                 block->setTempMod(v3s16(8,6,8), mod);*/
719 #if 0
720                                 /*
721                                         Add some coulds
722                                         Well, this is a dumb way to do it, they should just
723                                         be drawn as separate objects. But the looks of them
724                                         can be tested this way.
725                                 */
726                                 if(p.Y == 3)
727                                 {
728                                         NodeMod mod;
729                                         mod.type = NODEMOD_CHANGECONTENT;
730                                         mod.param = CONTENT_CLOUD;
731                                         v3s16 p2;
732                                         p2.Y = 8;
733                                         for(p2.X=3; p2.X<=13; p2.X++)
734                                         for(p2.Z=3; p2.Z<=13; p2.Z++)
735                                         {
736                                                 block->setTempMod(p2, mod);
737                                         }
738                                 }
739 #endif
740                         }
741                 } //envlock
742
743 #if 0
744                 /*
745                         Acknowledge block
746                 */
747                 /*
748                         [0] u16 command
749                         [2] u8 count
750                         [3] v3s16 pos_0
751                         [3+6] v3s16 pos_1
752                         ...
753                 */
754                 u32 replysize = 2+1+6;
755                 SharedBuffer<u8> reply(replysize);
756                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
757                 reply[2] = 1;
758                 writeV3S16(&reply[3], p);
759                 // Send as reliable
760                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
761 #endif
762
763                 /*
764                         Update Mesh of this block and blocks at x-, y- and z-.
765                         Environment should not be locked as it interlocks with the
766                         main thread, from which is will want to retrieve textures.
767                 */
768
769                 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
770                 
771                 addUpdateMeshTaskWithEdge(p, true);
772         }
773         else if(command == TOCLIENT_PLAYERPOS)
774         {
775                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
776                                 <<std::endl;
777                 /*u16 our_peer_id;
778                 {
779                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
780                         our_peer_id = m_con.GetPeerID();
781                 }
782                 // Cancel if we don't have a peer id
783                 if(our_peer_id == PEER_ID_INEXISTENT){
784                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
785                                         "we have no peer id"
786                                         <<std::endl;
787                         return;
788                 }*/
789
790                 { //envlock
791                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
792                         
793                         u32 player_size = 2+12+12+4+4;
794                                 
795                         u32 player_count = (datasize-2) / player_size;
796                         u32 start = 2;
797                         for(u32 i=0; i<player_count; i++)
798                         {
799                                 u16 peer_id = readU16(&data[start]);
800
801                                 Player *player = m_env.getPlayer(peer_id);
802
803                                 // Skip if player doesn't exist
804                                 if(player == NULL)
805                                 {
806                                         start += player_size;
807                                         continue;
808                                 }
809
810                                 // Skip if player is local player
811                                 if(player->isLocal())
812                                 {
813                                         start += player_size;
814                                         continue;
815                                 }
816
817                                 v3s32 ps = readV3S32(&data[start+2]);
818                                 v3s32 ss = readV3S32(&data[start+2+12]);
819                                 s32 pitch_i = readS32(&data[start+2+12+12]);
820                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
821                                 /*dstream<<"Client: got "
822                                                 <<"pitch_i="<<pitch_i
823                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
824                                 f32 pitch = (f32)pitch_i / 100.0;
825                                 f32 yaw = (f32)yaw_i / 100.0;
826                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
827                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
828                                 player->setPosition(position);
829                                 player->setSpeed(speed);
830                                 player->setPitch(pitch);
831                                 player->setYaw(yaw);
832
833                                 /*dstream<<"Client: player "<<peer_id
834                                                 <<" pitch="<<pitch
835                                                 <<" yaw="<<yaw<<std::endl;*/
836
837                                 start += player_size;
838                         }
839                 } //envlock
840         }
841         else if(command == TOCLIENT_PLAYERINFO)
842         {
843                 u16 our_peer_id;
844                 {
845                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
846                         our_peer_id = m_con.GetPeerID();
847                 }
848                 // Cancel if we don't have a peer id
849                 if(our_peer_id == PEER_ID_INEXISTENT){
850                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
851                                         "we have no peer id"
852                                         <<std::endl;
853                         return;
854                 }
855                 
856                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
857
858                 { //envlock
859                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
860                         
861                         u32 item_size = 2+PLAYERNAME_SIZE;
862                         u32 player_count = (datasize-2) / item_size;
863                         u32 start = 2;
864                         // peer_ids
865                         core::list<u16> players_alive;
866                         for(u32 i=0; i<player_count; i++)
867                         {
868                                 // Make sure the name ends in '\0'
869                                 data[start+2+20-1] = 0;
870
871                                 u16 peer_id = readU16(&data[start]);
872
873                                 players_alive.push_back(peer_id);
874                                 
875                                 /*dstream<<DTIME<<"peer_id="<<peer_id
876                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
877
878                                 // Don't update the info of the local player
879                                 if(peer_id == our_peer_id)
880                                 {
881                                         start += item_size;
882                                         continue;
883                                 }
884
885                                 Player *player = m_env.getPlayer(peer_id);
886
887                                 // Create a player if it doesn't exist
888                                 if(player == NULL)
889                                 {
890                                         player = new RemotePlayer(
891                                                         m_device->getSceneManager()->getRootSceneNode(),
892                                                         m_device,
893                                                         -1);
894                                         player->peer_id = peer_id;
895                                         m_env.addPlayer(player);
896                                         dout_client<<DTIME<<"Client: Adding new player "
897                                                         <<peer_id<<std::endl;
898                                 }
899                                 
900                                 player->updateName((char*)&data[start+2]);
901
902                                 start += item_size;
903                         }
904                         
905                         /*
906                                 Remove those players from the environment that
907                                 weren't listed by the server.
908                         */
909                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
910                         core::list<Player*> players = m_env.getPlayers();
911                         core::list<Player*>::Iterator ip;
912                         for(ip=players.begin(); ip!=players.end(); ip++)
913                         {
914                                 // Ingore local player
915                                 if((*ip)->isLocal())
916                                         continue;
917                                 
918                                 // Warn about a special case
919                                 if((*ip)->peer_id == 0)
920                                 {
921                                         dstream<<DTIME<<"WARNING: Client: Removing "
922                                                         "dead player with id=0"<<std::endl;
923                                 }
924
925                                 bool is_alive = false;
926                                 core::list<u16>::Iterator i;
927                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
928                                 {
929                                         if((*ip)->peer_id == *i)
930                                         {
931                                                 is_alive = true;
932                                                 break;
933                                         }
934                                 }
935                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
936                                                 <<" is_alive="<<is_alive<<std::endl;*/
937                                 if(is_alive)
938                                         continue;
939                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
940                                                 <<std::endl;
941                                 m_env.removePlayer((*ip)->peer_id);
942                         }
943                 } //envlock
944         }
945         else if(command == TOCLIENT_SECTORMETA)
946         {
947                 /*
948                         [0] u16 command
949                         [2] u8 sector count
950                         [3...] v2s16 pos + sector metadata
951                 */
952                 if(datasize < 3)
953                         return;
954
955                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
956
957                 { //envlock
958                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
959                         
960                         std::string datastring((char*)&data[2], datasize-2);
961                         std::istringstream is(datastring, std::ios_base::binary);
962
963                         u8 buf[4];
964
965                         is.read((char*)buf, 1);
966                         u16 sector_count = readU8(buf);
967                         
968                         //dstream<<"sector_count="<<sector_count<<std::endl;
969
970                         for(u16 i=0; i<sector_count; i++)
971                         {
972                                 // Read position
973                                 is.read((char*)buf, 4);
974                                 v2s16 pos = readV2S16(buf);
975                                 /*dstream<<"Client: deserializing sector at "
976                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
977                                 // Create sector
978                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
979                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
980                         }
981                 } //envlock
982         }
983         else if(command == TOCLIENT_INVENTORY)
984         {
985                 if(datasize < 3)
986                         return;
987
988                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
989
990                 { //envlock
991                         //TimeTaker t2("mutex locking", m_device);
992                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
993                         //t2.stop();
994                         
995                         //TimeTaker t3("istringstream init", m_device);
996                         std::string datastring((char*)&data[2], datasize-2);
997                         std::istringstream is(datastring, std::ios_base::binary);
998                         //t3.stop();
999                         
1000                         //m_env.printPlayers(dstream);
1001
1002                         //TimeTaker t4("player get", m_device);
1003                         Player *player = m_env.getLocalPlayer();
1004                         assert(player != NULL);
1005                         //t4.stop();
1006
1007                         //TimeTaker t1("inventory.deSerialize()", m_device);
1008                         player->inventory.deSerialize(is);
1009                         //t1.stop();
1010
1011                         m_inventory_updated = true;
1012
1013                         //dstream<<"Client got player inventory:"<<std::endl;
1014                         //player->inventory.print(dstream);
1015                 }
1016         }
1017         //DEBUG
1018         else if(command == TOCLIENT_OBJECTDATA)
1019         //else if(0)
1020         {
1021                 // Strip command word and create a stringstream
1022                 std::string datastring((char*)&data[2], datasize-2);
1023                 std::istringstream is(datastring, std::ios_base::binary);
1024                 
1025                 { //envlock
1026                 
1027                 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1028
1029                 u8 buf[12];
1030
1031                 /*
1032                         Read players
1033                 */
1034
1035                 is.read((char*)buf, 2);
1036                 u16 playercount = readU16(buf);
1037                 
1038                 for(u16 i=0; i<playercount; i++)
1039                 {
1040                         is.read((char*)buf, 2);
1041                         u16 peer_id = readU16(buf);
1042                         is.read((char*)buf, 12);
1043                         v3s32 p_i = readV3S32(buf);
1044                         is.read((char*)buf, 12);
1045                         v3s32 s_i = readV3S32(buf);
1046                         is.read((char*)buf, 4);
1047                         s32 pitch_i = readS32(buf);
1048                         is.read((char*)buf, 4);
1049                         s32 yaw_i = readS32(buf);
1050                         
1051                         Player *player = m_env.getPlayer(peer_id);
1052
1053                         // Skip if player doesn't exist
1054                         if(player == NULL)
1055                         {
1056                                 continue;
1057                         }
1058
1059                         // Skip if player is local player
1060                         if(player->isLocal())
1061                         {
1062                                 continue;
1063                         }
1064         
1065                         f32 pitch = (f32)pitch_i / 100.0;
1066                         f32 yaw = (f32)yaw_i / 100.0;
1067                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1068                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1069                         
1070                         player->setPosition(position);
1071                         player->setSpeed(speed);
1072                         player->setPitch(pitch);
1073                         player->setYaw(yaw);
1074                 }
1075
1076                 /*
1077                         Read block objects
1078                 */
1079
1080                 // Read active block count
1081                 is.read((char*)buf, 2);
1082                 u16 blockcount = readU16(buf);
1083                 
1084                 // Initialize delete queue with all active blocks
1085                 core::map<v3s16, bool> abs_to_delete;
1086                 for(core::map<v3s16, bool>::Iterator
1087                                 i = m_active_blocks.getIterator();
1088                                 i.atEnd() == false; i++)
1089                 {
1090                         v3s16 p = i.getNode()->getKey();
1091                         /*dstream<<"adding "
1092                                         <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1093                                         <<" to abs_to_delete"
1094                                         <<std::endl;*/
1095                         abs_to_delete.insert(p, true);
1096                 }
1097
1098                 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1099                                 <<std::endl;*/
1100                 
1101                 for(u16 i=0; i<blockcount; i++)
1102                 {
1103                         // Read blockpos
1104                         is.read((char*)buf, 6);
1105                         v3s16 p = readV3S16(buf);
1106                         // Get block from somewhere
1107                         MapBlock *block = NULL;
1108                         try{
1109                                 block = m_env.getMap().getBlockNoCreate(p);
1110                         }
1111                         catch(InvalidPositionException &e)
1112                         {
1113                                 //TODO: Create a dummy block?
1114                         }
1115                         if(block == NULL)
1116                         {
1117                                 dstream<<"WARNING: "
1118                                                 <<"Could not get block at blockpos "
1119                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1120                                                 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1121                                                 <<"following block object data."
1122                                                 <<std::endl;
1123                                 return;
1124                         }
1125
1126                         /*dstream<<"Client updating objects for block "
1127                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1128                                         <<std::endl;*/
1129
1130                         // Insert to active block list
1131                         m_active_blocks.insert(p, true);
1132
1133                         // Remove from deletion queue
1134                         if(abs_to_delete.find(p) != NULL)
1135                                 abs_to_delete.remove(p);
1136
1137                         /*
1138                                 Update objects of block
1139                                 
1140                                 NOTE: Be sure this is done in the main thread.
1141                         */
1142                         block->updateObjects(is, m_server_ser_ver,
1143                                         m_device->getSceneManager(), m_env.getDayNightRatio());
1144                 }
1145                 
1146                 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1147                                 <<std::endl;*/
1148                 
1149                 // Delete objects of blocks in delete queue
1150                 for(core::map<v3s16, bool>::Iterator
1151                                 i = abs_to_delete.getIterator();
1152                                 i.atEnd() == false; i++)
1153                 {
1154                         v3s16 p = i.getNode()->getKey();
1155                         try
1156                         {
1157                                 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1158                                 
1159                                 // Clear objects
1160                                 block->clearObjects();
1161                                 // Remove from active blocks list
1162                                 m_active_blocks.remove(p);
1163                         }
1164                         catch(InvalidPositionException &e)
1165                         {
1166                                 dstream<<"WARNAING: Client: "
1167                                                 <<"Couldn't clear objects of active->inactive"
1168                                                 <<" block "
1169                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1170                                                 <<" because block was not found"
1171                                                 <<std::endl;
1172                                 // Ignore
1173                         }
1174                 }
1175
1176                 } //envlock
1177         }
1178         else if(command == TOCLIENT_TIME_OF_DAY)
1179         {
1180                 if(datasize < 4)
1181                         return;
1182                 
1183                 u16 time = readU16(&data[2]);
1184                 time = time % 24000;
1185                 m_time_of_day = time;
1186                 //dstream<<"Client: time="<<time<<std::endl;
1187                 
1188                 /*
1189                         Day/night
1190
1191                         time_of_day:
1192                         0 = midnight
1193                         12000 = midday
1194                 */
1195                 {
1196                         u32 dr = time_to_daynight_ratio(m_time_of_day);
1197
1198                         dstream<<"Client: time_of_day="<<m_time_of_day
1199                                         <<", dr="<<dr
1200                                         <<std::endl;
1201                         
1202                         if(dr != m_env.getDayNightRatio())
1203                         {
1204                                 dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
1205                                 m_env.setDayNightRatio(dr);
1206                                 m_env.expireMeshes(true);
1207                         }
1208                 }
1209
1210         }
1211         else if(command == TOCLIENT_CHAT_MESSAGE)
1212         {
1213                 /*
1214                         u16 command
1215                         u16 length
1216                         wstring message
1217                 */
1218                 u8 buf[6];
1219                 std::string datastring((char*)&data[2], datasize-2);
1220                 std::istringstream is(datastring, std::ios_base::binary);
1221                 
1222                 // Read stuff
1223                 is.read((char*)buf, 2);
1224                 u16 len = readU16(buf);
1225                 
1226                 std::wstring message;
1227                 for(u16 i=0; i<len; i++)
1228                 {
1229                         is.read((char*)buf, 2);
1230                         message += (wchar_t)readU16(buf);
1231                 }
1232
1233                 /*dstream<<"Client received chat message: "
1234                                 <<wide_to_narrow(message)<<std::endl;*/
1235                 
1236                 m_chat_queue.push_back(message);
1237         }
1238         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1239         {
1240                 //if(g_settings.getBool("enable_experimental"))
1241                 {
1242                         /*
1243                                 u16 command
1244                                 u16 count of removed objects
1245                                 for all removed objects {
1246                                         u16 id
1247                                 }
1248                                 u16 count of added objects
1249                                 for all added objects {
1250                                         u16 id
1251                                         u8 type
1252                                         u16 initialization data length
1253                                         string initialization data
1254                                 }
1255                         */
1256
1257                         char buf[6];
1258                         // Get all data except the command number
1259                         std::string datastring((char*)&data[2], datasize-2);
1260                         // Throw them in an istringstream
1261                         std::istringstream is(datastring, std::ios_base::binary);
1262
1263                         // Read stuff
1264                         
1265                         // Read removed objects
1266                         is.read(buf, 2);
1267                         u16 removed_count = readU16((u8*)buf);
1268                         for(u16 i=0; i<removed_count; i++)
1269                         {
1270                                 is.read(buf, 2);
1271                                 u16 id = readU16((u8*)buf);
1272                                 // Remove it
1273                                 {
1274                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1275                                         m_env.removeActiveObject(id);
1276                                 }
1277                         }
1278                         
1279                         // Read added objects
1280                         is.read(buf, 2);
1281                         u16 added_count = readU16((u8*)buf);
1282                         for(u16 i=0; i<added_count; i++)
1283                         {
1284                                 is.read(buf, 2);
1285                                 u16 id = readU16((u8*)buf);
1286                                 is.read(buf, 1);
1287                                 u8 type = readU8((u8*)buf);
1288                                 std::string data = deSerializeLongString(is);
1289                                 // Add it
1290                                 {
1291                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1292                                         m_env.addActiveObject(id, type, data);
1293                                 }
1294                         }
1295                 }
1296         }
1297         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1298         {
1299                 //if(g_settings.getBool("enable_experimental"))
1300                 {
1301                         /*
1302                                 u16 command
1303                                 for all objects
1304                                 {
1305                                         u16 id
1306                                         u16 message length
1307                                         string message
1308                                 }
1309                         */
1310                         char buf[6];
1311                         // Get all data except the command number
1312                         std::string datastring((char*)&data[2], datasize-2);
1313                         // Throw them in an istringstream
1314                         std::istringstream is(datastring, std::ios_base::binary);
1315                         
1316                         while(is.eof() == false)
1317                         {
1318                                 // Read stuff
1319                                 is.read(buf, 2);
1320                                 u16 id = readU16((u8*)buf);
1321                                 if(is.eof())
1322                                         break;
1323                                 is.read(buf, 2);
1324                                 u16 message_size = readU16((u8*)buf);
1325                                 std::string message;
1326                                 message.reserve(message_size);
1327                                 for(u16 i=0; i<message_size; i++)
1328                                 {
1329                                         is.read(buf, 1);
1330                                         message.append(buf, 1);
1331                                 }
1332                                 // Pass on to the environment
1333                                 {
1334                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1335                                         m_env.processActiveObjectMessage(id, message);
1336                                 }
1337                         }
1338                 }
1339         }
1340         else if(command == TOCLIENT_HP)
1341         {
1342                 std::string datastring((char*)&data[2], datasize-2);
1343                 std::istringstream is(datastring, std::ios_base::binary);
1344                 Player *player = m_env.getLocalPlayer();
1345                 assert(player != NULL);
1346                 u8 hp = readU8(is);
1347                 player->hp = hp;
1348         }
1349         else if(command == TOCLIENT_MOVE_PLAYER)
1350         {
1351                 std::string datastring((char*)&data[2], datasize-2);
1352                 std::istringstream is(datastring, std::ios_base::binary);
1353                 Player *player = m_env.getLocalPlayer();
1354                 assert(player != NULL);
1355                 v3f pos = readV3F1000(is);
1356                 f32 pitch = readF1000(is);
1357                 f32 yaw = readF1000(is);
1358                 player->setPosition(pos);
1359                 /*player->setPitch(pitch);
1360                 player->setYaw(yaw);*/
1361
1362                 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1363                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1364                                 <<" pitch="<<pitch
1365                                 <<" yaw="<<yaw
1366                                 <<std::endl;
1367
1368                 /*
1369                         Add to ClientEvent queue.
1370                         This has to be sent to the main program because otherwise
1371                         it would just force the pitch and yaw values to whatever
1372                         the camera points to.
1373                 */
1374                 ClientEvent event;
1375                 event.type = CE_PLAYER_FORCE_MOVE;
1376                 event.player_force_move.pitch = pitch;
1377                 event.player_force_move.yaw = yaw;
1378                 m_client_event_queue.push_back(event);
1379
1380                 // Ignore damage for a few seconds, so that the player doesn't
1381                 // get damage from falling on ground
1382                 m_ignore_damage_timer = 3.0;
1383         }
1384         else
1385         {
1386                 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1387                                 <<command<<std::endl;
1388         }
1389 }
1390
1391 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1392 {
1393         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1394         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1395 }
1396
1397 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1398                 v3s16 nodepos_oversurface, u16 item)
1399 {
1400         if(connectedAndInitialized() == false){
1401                 dout_client<<DTIME<<"Client::groundAction() "
1402                                 "cancelled (not connected)"
1403                                 <<std::endl;
1404                 return;
1405         }
1406         
1407         /*
1408                 length: 17
1409                 [0] u16 command
1410                 [2] u8 action
1411                 [3] v3s16 nodepos_undersurface
1412                 [9] v3s16 nodepos_abovesurface
1413                 [15] u16 item
1414                 actions:
1415                 0: start digging
1416                 1: place block
1417                 2: stop digging (all parameters ignored)
1418                 3: digging completed
1419         */
1420         u8 datasize = 2 + 1 + 6 + 6 + 2;
1421         SharedBuffer<u8> data(datasize);
1422         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1423         writeU8(&data[2], action);
1424         writeV3S16(&data[3], nodepos_undersurface);
1425         writeV3S16(&data[9], nodepos_oversurface);
1426         writeU16(&data[15], item);
1427         Send(0, data, true);
1428 }
1429
1430 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1431 {
1432         if(connectedAndInitialized() == false){
1433                 dout_client<<DTIME<<"Client::clickObject() "
1434                                 "cancelled (not connected)"
1435                                 <<std::endl;
1436                 return;
1437         }
1438         
1439         /*
1440                 [0] u16 command=TOSERVER_CLICK_OBJECT
1441                 [2] u8 button (0=left, 1=right)
1442                 [3] v3s16 block
1443                 [9] s16 id
1444                 [11] u16 item
1445         */
1446         u8 datasize = 2 + 1 + 6 + 2 + 2;
1447         SharedBuffer<u8> data(datasize);
1448         writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1449         writeU8(&data[2], button);
1450         writeV3S16(&data[3], blockpos);
1451         writeS16(&data[9], id);
1452         writeU16(&data[11], item);
1453         Send(0, data, true);
1454 }
1455
1456 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1457 {
1458         if(connectedAndInitialized() == false){
1459                 dout_client<<DTIME<<"Client::clickActiveObject() "
1460                                 "cancelled (not connected)"
1461                                 <<std::endl;
1462                 return;
1463         }
1464         
1465         /*
1466                 length: 7
1467                 [0] u16 command
1468                 [2] u8 button (0=left, 1=right)
1469                 [3] u16 id
1470                 [5] u16 item
1471         */
1472         u8 datasize = 2 + 1 + 6 + 2 + 2;
1473         SharedBuffer<u8> data(datasize);
1474         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1475         writeU8(&data[2], button);
1476         writeU16(&data[3], id);
1477         writeU16(&data[5], item);
1478         Send(0, data, true);
1479 }
1480
1481 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1482 {
1483         /*
1484                 u16 command
1485                 v3s16 blockpos
1486                 s16 id
1487                 u16 textlen
1488                 textdata
1489         */
1490         std::ostringstream os(std::ios_base::binary);
1491         u8 buf[12];
1492         
1493         // Write command
1494         writeU16(buf, TOSERVER_SIGNTEXT);
1495         os.write((char*)buf, 2);
1496         
1497         // Write blockpos
1498         writeV3S16(buf, blockpos);
1499         os.write((char*)buf, 6);
1500
1501         // Write id
1502         writeS16(buf, id);
1503         os.write((char*)buf, 2);
1504
1505         u16 textlen = text.size();
1506         // Write text length
1507         writeS16(buf, textlen);
1508         os.write((char*)buf, 2);
1509
1510         // Write text
1511         os.write((char*)text.c_str(), textlen);
1512         
1513         // Make data buffer
1514         std::string s = os.str();
1515         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1516         // Send as reliable
1517         Send(0, data, true);
1518 }
1519         
1520 void Client::sendSignNodeText(v3s16 p, std::string text)
1521 {
1522         /*
1523                 u16 command
1524                 v3s16 p
1525                 u16 textlen
1526                 textdata
1527         */
1528         std::ostringstream os(std::ios_base::binary);
1529         u8 buf[12];
1530         
1531         // Write command
1532         writeU16(buf, TOSERVER_SIGNNODETEXT);
1533         os.write((char*)buf, 2);
1534         
1535         // Write p
1536         writeV3S16(buf, p);
1537         os.write((char*)buf, 6);
1538
1539         u16 textlen = text.size();
1540         // Write text length
1541         writeS16(buf, textlen);
1542         os.write((char*)buf, 2);
1543
1544         // Write text
1545         os.write((char*)text.c_str(), textlen);
1546         
1547         // Make data buffer
1548         std::string s = os.str();
1549         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1550         // Send as reliable
1551         Send(0, data, true);
1552 }
1553         
1554 void Client::sendInventoryAction(InventoryAction *a)
1555 {
1556         std::ostringstream os(std::ios_base::binary);
1557         u8 buf[12];
1558         
1559         // Write command
1560         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1561         os.write((char*)buf, 2);
1562
1563         a->serialize(os);
1564         
1565         // Make data buffer
1566         std::string s = os.str();
1567         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1568         // Send as reliable
1569         Send(0, data, true);
1570 }
1571
1572 void Client::sendChatMessage(const std::wstring &message)
1573 {
1574         std::ostringstream os(std::ios_base::binary);
1575         u8 buf[12];
1576         
1577         // Write command
1578         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1579         os.write((char*)buf, 2);
1580         
1581         // Write length
1582         writeU16(buf, message.size());
1583         os.write((char*)buf, 2);
1584         
1585         // Write string
1586         for(u32 i=0; i<message.size(); i++)
1587         {
1588                 u16 w = message[i];
1589                 writeU16(buf, w);
1590                 os.write((char*)buf, 2);
1591         }
1592         
1593         // Make data buffer
1594         std::string s = os.str();
1595         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1596         // Send as reliable
1597         Send(0, data, true);
1598 }
1599
1600 void Client::sendDamage(u8 damage)
1601 {
1602         DSTACK(__FUNCTION_NAME);
1603         std::ostringstream os(std::ios_base::binary);
1604
1605         writeU16(os, TOSERVER_DAMAGE);
1606         writeU8(os, damage);
1607
1608         // Make data buffer
1609         std::string s = os.str();
1610         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1611         // Send as reliable
1612         Send(0, data, true);
1613 }
1614
1615 void Client::sendPlayerPos()
1616 {
1617         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1618         
1619         Player *myplayer = m_env.getLocalPlayer();
1620         if(myplayer == NULL)
1621                 return;
1622         
1623         u16 our_peer_id;
1624         {
1625                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1626                 our_peer_id = m_con.GetPeerID();
1627         }
1628         
1629         // Set peer id if not set already
1630         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1631                 myplayer->peer_id = our_peer_id;
1632         // Check that an existing peer_id is the same as the connection's
1633         assert(myplayer->peer_id == our_peer_id);
1634         
1635         v3f pf = myplayer->getPosition();
1636         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1637         v3f sf = myplayer->getSpeed();
1638         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1639         s32 pitch = myplayer->getPitch() * 100;
1640         s32 yaw = myplayer->getYaw() * 100;
1641
1642         /*
1643                 Format:
1644                 [0] u16 command
1645                 [2] v3s32 position*100
1646                 [2+12] v3s32 speed*100
1647                 [2+12+12] s32 pitch*100
1648                 [2+12+12+4] s32 yaw*100
1649         */
1650
1651         SharedBuffer<u8> data(2+12+12+4+4);
1652         writeU16(&data[0], TOSERVER_PLAYERPOS);
1653         writeV3S32(&data[2], position);
1654         writeV3S32(&data[2+12], speed);
1655         writeS32(&data[2+12+12], pitch);
1656         writeS32(&data[2+12+12+4], yaw);
1657
1658         // Send as unreliable
1659         Send(0, data, false);
1660 }
1661
1662 void Client::removeNode(v3s16 p)
1663 {
1664         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1665         
1666         core::map<v3s16, MapBlock*> modified_blocks;
1667
1668         try
1669         {
1670                 //TimeTaker t("removeNodeAndUpdate", m_device);
1671                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1672         }
1673         catch(InvalidPositionException &e)
1674         {
1675         }
1676         
1677         for(core::map<v3s16, MapBlock * >::Iterator
1678                         i = modified_blocks.getIterator();
1679                         i.atEnd() == false; i++)
1680         {
1681                 v3s16 p = i.getNode()->getKey();
1682                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1683                 addUpdateMeshTaskWithEdge(p);
1684         }
1685 }
1686
1687 void Client::addNode(v3s16 p, MapNode n)
1688 {
1689         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1690
1691         TimeTaker timer1("Client::addNode()");
1692
1693         core::map<v3s16, MapBlock*> modified_blocks;
1694
1695         try
1696         {
1697                 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1698                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1699         }
1700         catch(InvalidPositionException &e)
1701         {}
1702         
1703         //TimeTaker timer2("Client::addNode(): updateMeshes");
1704
1705         for(core::map<v3s16, MapBlock * >::Iterator
1706                         i = modified_blocks.getIterator();
1707                         i.atEnd() == false; i++)
1708         {
1709                 v3s16 p = i.getNode()->getKey();
1710                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1711                 addUpdateMeshTaskWithEdge(p);
1712         }
1713 }
1714         
1715 void Client::updateCamera(v3f pos, v3f dir)
1716 {
1717         m_env.getClientMap().updateCamera(pos, dir);
1718         camera_position = pos;
1719         camera_direction = dir;
1720 }
1721
1722 MapNode Client::getNode(v3s16 p)
1723 {
1724         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1725         return m_env.getMap().getNode(p);
1726 }
1727
1728 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1729 {
1730         return m_env.getMap().getNodeMetadata(p);
1731 }
1732
1733 v3f Client::getPlayerPosition()
1734 {
1735         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1736         LocalPlayer *player = m_env.getLocalPlayer();
1737         assert(player != NULL);
1738         return player->getPosition();
1739 }
1740
1741 void Client::setPlayerControl(PlayerControl &control)
1742 {
1743         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1744         LocalPlayer *player = m_env.getLocalPlayer();
1745         assert(player != NULL);
1746         player->control = control;
1747 }
1748
1749 // Returns true if the inventory of the local player has been
1750 // updated from the server. If it is true, it is set to false.
1751 bool Client::getLocalInventoryUpdated()
1752 {
1753         // m_inventory_updated is behind envlock
1754         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1755         bool updated = m_inventory_updated;
1756         m_inventory_updated = false;
1757         return updated;
1758 }
1759
1760 // Copies the inventory of the local player to parameter
1761 void Client::getLocalInventory(Inventory &dst)
1762 {
1763         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1764         Player *player = m_env.getLocalPlayer();
1765         assert(player != NULL);
1766         dst = player->inventory;
1767 }
1768
1769 InventoryContext *Client::getInventoryContext()
1770 {
1771         return &m_inventory_context;
1772 }
1773
1774 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1775 {
1776         if(id == "current_player")
1777         {
1778                 assert(c->current_player);
1779                 return &(c->current_player->inventory);
1780         }
1781         
1782         Strfnd fn(id);
1783         std::string id0 = fn.next(":");
1784
1785         if(id0 == "nodemeta")
1786         {
1787                 v3s16 p;
1788                 p.X = stoi(fn.next(","));
1789                 p.Y = stoi(fn.next(","));
1790                 p.Z = stoi(fn.next(","));
1791                 NodeMetadata* meta = getNodeMetadata(p);
1792                 if(meta)
1793                         return meta->getInventory();
1794                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1795                                 <<"no metadata found"<<std::endl;
1796                 return NULL;
1797         }
1798
1799         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1800         return NULL;
1801 }
1802 void Client::inventoryAction(InventoryAction *a)
1803 {
1804         sendInventoryAction(a);
1805 }
1806
1807 MapBlockObject * Client::getSelectedObject(
1808                 f32 max_d,
1809                 v3f from_pos_f_on_map,
1810                 core::line3d<f32> shootline_on_map
1811         )
1812 {
1813         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1814
1815         core::array<DistanceSortedObject> objects;
1816
1817         for(core::map<v3s16, bool>::Iterator
1818                         i = m_active_blocks.getIterator();
1819                         i.atEnd() == false; i++)
1820         {
1821                 v3s16 p = i.getNode()->getKey();
1822
1823                 MapBlock *block = NULL;
1824                 try
1825                 {
1826                         block = m_env.getMap().getBlockNoCreate(p);
1827                 }
1828                 catch(InvalidPositionException &e)
1829                 {
1830                         continue;
1831                 }
1832
1833                 // Calculate from_pos relative to block
1834                 v3s16 block_pos_i_on_map = block->getPosRelative();
1835                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1836                 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1837
1838                 block->getObjects(from_pos_f_on_block, max_d, objects);
1839                 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1840         }
1841
1842         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1843         
1844         // Sort them.
1845         // After this, the closest object is the first in the array.
1846         objects.sort();
1847
1848         for(u32 i=0; i<objects.size(); i++)
1849         {
1850                 MapBlockObject *obj = objects[i].obj;
1851                 MapBlock *block = obj->getBlock();
1852
1853                 // Calculate shootline relative to block
1854                 v3s16 block_pos_i_on_map = block->getPosRelative();
1855                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1856                 core::line3d<f32> shootline_on_block(
1857                                 shootline_on_map.start - block_pos_f_on_map,
1858                                 shootline_on_map.end - block_pos_f_on_map
1859                 );
1860
1861                 if(obj->isSelected(shootline_on_block))
1862                 {
1863                         //dstream<<"Returning selected object"<<std::endl;
1864                         return obj;
1865                 }
1866         }
1867
1868         //dstream<<"No object selected; returning NULL."<<std::endl;
1869         return NULL;
1870 }
1871
1872 ClientActiveObject * Client::getSelectedActiveObject(
1873                 f32 max_d,
1874                 v3f from_pos_f_on_map,
1875                 core::line3d<f32> shootline_on_map
1876         )
1877 {
1878         core::array<DistanceSortedActiveObject> objects;
1879
1880         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1881
1882         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1883         
1884         // Sort them.
1885         // After this, the closest object is the first in the array.
1886         objects.sort();
1887
1888         for(u32 i=0; i<objects.size(); i++)
1889         {
1890                 ClientActiveObject *obj = objects[i].obj;
1891                 
1892                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1893                 if(selection_box == NULL)
1894                         continue;
1895
1896                 v3f pos = obj->getPosition();
1897
1898                 core::aabbox3d<f32> offsetted_box(
1899                                 selection_box->MinEdge + pos,
1900                                 selection_box->MaxEdge + pos
1901                 );
1902
1903                 if(offsetted_box.intersectsWithLine(shootline_on_map))
1904                 {
1905                         //dstream<<"Returning selected object"<<std::endl;
1906                         return obj;
1907                 }
1908         }
1909
1910         //dstream<<"No object selected; returning NULL."<<std::endl;
1911         return NULL;
1912 }
1913
1914 void Client::printDebugInfo(std::ostream &os)
1915 {
1916         //JMutexAutoLock lock1(m_fetchblock_mutex);
1917         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1918
1919         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1920                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1921                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1922                 <<std::endl;*/
1923 }
1924         
1925 /*s32 Client::getDayNightIndex()
1926 {
1927         assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1928         return m_daynight_i;
1929 }*/
1930
1931 u32 Client::getDayNightRatio()
1932 {
1933         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1934         return m_env.getDayNightRatio();
1935 }
1936
1937 u16 Client::getHP()
1938 {
1939         Player *player = m_env.getLocalPlayer();
1940         assert(player != NULL);
1941         return player->hp;
1942 }
1943
1944 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1945 {
1946         /*dstream<<"Client::addUpdateMeshTask(): "
1947                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1948                         <<std::endl;*/
1949
1950         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
1951         if(b == NULL)
1952                 return;
1953
1954         /*
1955                 Create a task to update the mesh of the block
1956         */
1957         
1958         MeshMakeData *data = new MeshMakeData;
1959         
1960         {
1961                 //TimeTaker timer("data fill");
1962                 // 0ms
1963                 data->fill(getDayNightRatio(), b);
1964         }
1965
1966         // Debug wait
1967         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
1968         
1969         // Add task to queue
1970         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
1971
1972         /*dstream<<"Mesh update input queue size is "
1973                         <<m_mesh_update_thread.m_queue_in.size()
1974                         <<std::endl;*/
1975         
1976 #if 0
1977         // Temporary test: make mesh directly in here
1978         {
1979                 //TimeTaker timer("make mesh");
1980                 // 10ms
1981                 scene::SMesh *mesh_new = NULL;
1982                 mesh_new = makeMapBlockMesh(data);
1983                 b->replaceMesh(mesh_new);
1984                 delete data;
1985         }
1986 #endif
1987
1988         b->setMeshExpired(false);
1989 }
1990
1991 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
1992 {
1993         /*{
1994                 v3s16 p = blockpos;
1995                 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
1996                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1997                                 <<std::endl;
1998         }*/
1999
2000         try{
2001                 v3s16 p = blockpos + v3s16(0,0,0);
2002                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2003                 addUpdateMeshTask(p, ack_to_server);
2004         }
2005         catch(InvalidPositionException &e){}
2006         // Leading edge
2007         try{
2008                 v3s16 p = blockpos + v3s16(-1,0,0);
2009                 addUpdateMeshTask(p);
2010         }
2011         catch(InvalidPositionException &e){}
2012         try{
2013                 v3s16 p = blockpos + v3s16(0,-1,0);
2014                 addUpdateMeshTask(p);
2015         }
2016         catch(InvalidPositionException &e){}
2017         try{
2018                 v3s16 p = blockpos + v3s16(0,0,-1);
2019                 addUpdateMeshTask(p);
2020         }
2021         catch(InvalidPositionException &e){}
2022 }
2023
2024 ClientEvent Client::getClientEvent()
2025 {
2026         if(m_client_event_queue.size() == 0)
2027         {
2028                 ClientEvent event;
2029                 event.type = CE_NONE;
2030                 return event;
2031         }
2032         return m_client_event_queue.pop_front();
2033 }
2034
2035