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