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