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