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