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