Do not expose CONTENT_* stuff in content_mapnode.h and use a name converter wrapper...
[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);
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
905 #if 0
906                 /*
907                         Acknowledge block
908                 */
909                 /*
910                         [0] u16 command
911                         [2] u8 count
912                         [3] v3s16 pos_0
913                         [3+6] v3s16 pos_1
914                         ...
915                 */
916                 u32 replysize = 2+1+6;
917                 SharedBuffer<u8> reply(replysize);
918                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
919                 reply[2] = 1;
920                 writeV3S16(&reply[3], p);
921                 // Send as reliable
922                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
923 #endif
924
925                 /*
926                         Update Mesh of this block and blocks at x-, y- and z-.
927                         Environment should not be locked as it interlocks with the
928                         main thread, from which is will want to retrieve textures.
929                 */
930
931                 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
932                 /*
933                         Add it to mesh update queue and set it to be acknowledged after update.
934                 */
935                 //infostream<<"Adding mesh update task for received block"<<std::endl;
936                 addUpdateMeshTaskWithEdge(p, true);
937         }
938         else if(command == TOCLIENT_PLAYERPOS)
939         {
940                 infostream<<"Received deprecated TOCLIENT_PLAYERPOS"
941                                 <<std::endl;
942                 /*u16 our_peer_id;
943                 {
944                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
945                         our_peer_id = m_con.GetPeerID();
946                 }
947                 // Cancel if we don't have a peer id
948                 if(our_peer_id == PEER_ID_INEXISTENT){
949                         infostream<<"TOCLIENT_PLAYERPOS cancelled: "
950                                         "we have no peer id"
951                                         <<std::endl;
952                         return;
953                 }*/
954
955                 { //envlock
956                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
957                         
958                         u32 player_size = 2+12+12+4+4;
959                                 
960                         u32 player_count = (datasize-2) / player_size;
961                         u32 start = 2;
962                         for(u32 i=0; i<player_count; i++)
963                         {
964                                 u16 peer_id = readU16(&data[start]);
965
966                                 Player *player = m_env.getPlayer(peer_id);
967
968                                 // Skip if player doesn't exist
969                                 if(player == NULL)
970                                 {
971                                         start += player_size;
972                                         continue;
973                                 }
974
975                                 // Skip if player is local player
976                                 if(player->isLocal())
977                                 {
978                                         start += player_size;
979                                         continue;
980                                 }
981
982                                 v3s32 ps = readV3S32(&data[start+2]);
983                                 v3s32 ss = readV3S32(&data[start+2+12]);
984                                 s32 pitch_i = readS32(&data[start+2+12+12]);
985                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
986                                 /*infostream<<"Client: got "
987                                                 <<"pitch_i="<<pitch_i
988                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
989                                 f32 pitch = (f32)pitch_i / 100.0;
990                                 f32 yaw = (f32)yaw_i / 100.0;
991                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
992                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
993                                 player->setPosition(position);
994                                 player->setSpeed(speed);
995                                 player->setPitch(pitch);
996                                 player->setYaw(yaw);
997
998                                 /*infostream<<"Client: player "<<peer_id
999                                                 <<" pitch="<<pitch
1000                                                 <<" yaw="<<yaw<<std::endl;*/
1001
1002                                 start += player_size;
1003                         }
1004                 } //envlock
1005         }
1006         else if(command == TOCLIENT_PLAYERINFO)
1007         {
1008                 u16 our_peer_id;
1009                 {
1010                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1011                         our_peer_id = m_con.GetPeerID();
1012                 }
1013                 // Cancel if we don't have a peer id
1014                 if(our_peer_id == PEER_ID_INEXISTENT){
1015                         infostream<<"TOCLIENT_PLAYERINFO cancelled: "
1016                                         "we have no peer id"
1017                                         <<std::endl;
1018                         return;
1019                 }
1020                 
1021                 //infostream<<"Client: Server reports players:"<<std::endl;
1022
1023                 { //envlock
1024                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1025                         
1026                         u32 item_size = 2+PLAYERNAME_SIZE;
1027                         u32 player_count = (datasize-2) / item_size;
1028                         u32 start = 2;
1029                         // peer_ids
1030                         core::list<u16> players_alive;
1031                         for(u32 i=0; i<player_count; i++)
1032                         {
1033                                 // Make sure the name ends in '\0'
1034                                 data[start+2+20-1] = 0;
1035
1036                                 u16 peer_id = readU16(&data[start]);
1037
1038                                 players_alive.push_back(peer_id);
1039                                 
1040                                 /*infostream<<"peer_id="<<peer_id
1041                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
1042
1043                                 // Don't update the info of the local player
1044                                 if(peer_id == our_peer_id)
1045                                 {
1046                                         start += item_size;
1047                                         continue;
1048                                 }
1049
1050                                 Player *player = m_env.getPlayer(peer_id);
1051
1052                                 // Create a player if it doesn't exist
1053                                 if(player == NULL)
1054                                 {
1055                                         player = new RemotePlayer(this,
1056                                                         m_device->getSceneManager()->getRootSceneNode(),
1057                                                         m_device,
1058                                                         -1);
1059                                         player->peer_id = peer_id;
1060                                         m_env.addPlayer(player);
1061                                         infostream<<"Client: Adding new player "
1062                                                         <<peer_id<<std::endl;
1063                                 }
1064                                 
1065                                 player->updateName((char*)&data[start+2]);
1066
1067                                 start += item_size;
1068                         }
1069                         
1070                         /*
1071                                 Remove those players from the environment that
1072                                 weren't listed by the server.
1073                         */
1074                         //infostream<<"Removing dead players"<<std::endl;
1075                         core::list<Player*> players = m_env.getPlayers();
1076                         core::list<Player*>::Iterator ip;
1077                         for(ip=players.begin(); ip!=players.end(); ip++)
1078                         {
1079                                 // Ingore local player
1080                                 if((*ip)->isLocal())
1081                                         continue;
1082                                 
1083                                 // Warn about a special case
1084                                 if((*ip)->peer_id == 0)
1085                                 {
1086                                         infostream<<"Client: Removing "
1087                                                         "dead player with id=0"<<std::endl;
1088                                 }
1089
1090                                 bool is_alive = false;
1091                                 core::list<u16>::Iterator i;
1092                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
1093                                 {
1094                                         if((*ip)->peer_id == *i)
1095                                         {
1096                                                 is_alive = true;
1097                                                 break;
1098                                         }
1099                                 }
1100                                 /*infostream<<"peer_id="<<((*ip)->peer_id)
1101                                                 <<" is_alive="<<is_alive<<std::endl;*/
1102                                 if(is_alive)
1103                                         continue;
1104                                 infostream<<"Removing dead player "<<(*ip)->peer_id
1105                                                 <<std::endl;
1106                                 m_env.removePlayer((*ip)->peer_id);
1107                         }
1108                 } //envlock
1109         }
1110         else if(command == TOCLIENT_SECTORMETA)
1111         {
1112                 infostream<<"Client received DEPRECATED TOCLIENT_SECTORMETA"<<std::endl;
1113 #if 0
1114                 /*
1115                         [0] u16 command
1116                         [2] u8 sector count
1117                         [3...] v2s16 pos + sector metadata
1118                 */
1119                 if(datasize < 3)
1120                         return;
1121
1122                 //infostream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
1123
1124                 { //envlock
1125                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1126                         
1127                         std::string datastring((char*)&data[2], datasize-2);
1128                         std::istringstream is(datastring, std::ios_base::binary);
1129
1130                         u8 buf[4];
1131
1132                         is.read((char*)buf, 1);
1133                         u16 sector_count = readU8(buf);
1134                         
1135                         //infostream<<"sector_count="<<sector_count<<std::endl;
1136
1137                         for(u16 i=0; i<sector_count; i++)
1138                         {
1139                                 // Read position
1140                                 is.read((char*)buf, 4);
1141                                 v2s16 pos = readV2S16(buf);
1142                                 /*infostream<<"Client: deserializing sector at "
1143                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
1144                                 // Create sector
1145                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
1146                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1147                         }
1148                 } //envlock
1149 #endif
1150         }
1151         else if(command == TOCLIENT_INVENTORY)
1152         {
1153                 if(datasize < 3)
1154                         return;
1155
1156                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1157
1158                 { //envlock
1159                         //TimeTaker t2("mutex locking", m_device);
1160                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1161                         //t2.stop();
1162                         
1163                         //TimeTaker t3("istringstream init", m_device);
1164                         std::string datastring((char*)&data[2], datasize-2);
1165                         std::istringstream is(datastring, std::ios_base::binary);
1166                         //t3.stop();
1167                         
1168                         //m_env.printPlayers(infostream);
1169
1170                         //TimeTaker t4("player get", m_device);
1171                         Player *player = m_env.getLocalPlayer();
1172                         assert(player != NULL);
1173                         //t4.stop();
1174
1175                         //TimeTaker t1("inventory.deSerialize()", m_device);
1176                         player->inventory.deSerialize(is, this);
1177                         //t1.stop();
1178
1179                         m_inventory_updated = true;
1180
1181                         //infostream<<"Client got player inventory:"<<std::endl;
1182                         //player->inventory.print(infostream);
1183                 }
1184         }
1185         //DEBUG
1186         else if(command == TOCLIENT_OBJECTDATA)
1187         {
1188                 // Strip command word and create a stringstream
1189                 std::string datastring((char*)&data[2], datasize-2);
1190                 std::istringstream is(datastring, std::ios_base::binary);
1191                 
1192                 u8 buf[12];
1193
1194                 /*
1195                         Read players
1196                 */
1197
1198                 is.read((char*)buf, 2);
1199                 u16 playercount = readU16(buf);
1200                 
1201                 for(u16 i=0; i<playercount; i++)
1202                 {
1203                         is.read((char*)buf, 2);
1204                         u16 peer_id = readU16(buf);
1205                         is.read((char*)buf, 12);
1206                         v3s32 p_i = readV3S32(buf);
1207                         is.read((char*)buf, 12);
1208                         v3s32 s_i = readV3S32(buf);
1209                         is.read((char*)buf, 4);
1210                         s32 pitch_i = readS32(buf);
1211                         is.read((char*)buf, 4);
1212                         s32 yaw_i = readS32(buf);
1213                         
1214                         Player *player = m_env.getPlayer(peer_id);
1215
1216                         // Skip if player doesn't exist
1217                         if(player == NULL)
1218                         {
1219                                 continue;
1220                         }
1221
1222                         // Skip if player is local player
1223                         if(player->isLocal())
1224                         {
1225                                 continue;
1226                         }
1227         
1228                         f32 pitch = (f32)pitch_i / 100.0;
1229                         f32 yaw = (f32)yaw_i / 100.0;
1230                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1231                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1232                         
1233                         player->setPosition(position);
1234                         player->setSpeed(speed);
1235                         player->setPitch(pitch);
1236                         player->setYaw(yaw);
1237                 }
1238
1239                 /*
1240                         Read block objects
1241                         NOTE: Deprecated stuff
1242                 */
1243
1244                 // Read active block count
1245                 u16 blockcount = readU16(is);
1246                 if(blockcount != 0){
1247                         infostream<<"TOCLIENT_OBJECTDATA: blockcount != 0 "
1248                                         "not supported"<<std::endl;
1249                         return;
1250                 }
1251         }
1252         else if(command == TOCLIENT_TIME_OF_DAY)
1253         {
1254                 if(datasize < 4)
1255                         return;
1256                 
1257                 u16 time_of_day = readU16(&data[2]);
1258                 time_of_day = time_of_day % 24000;
1259                 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1260                 
1261                 /*
1262                         time_of_day:
1263                         0 = midnight
1264                         12000 = midday
1265                 */
1266                 {
1267                         m_env.setTimeOfDay(time_of_day);
1268
1269                         u32 dr = m_env.getDayNightRatio();
1270
1271                         infostream<<"Client: time_of_day="<<time_of_day
1272                                         <<", dr="<<dr
1273                                         <<std::endl;
1274                 }
1275
1276         }
1277         else if(command == TOCLIENT_CHAT_MESSAGE)
1278         {
1279                 /*
1280                         u16 command
1281                         u16 length
1282                         wstring message
1283                 */
1284                 u8 buf[6];
1285                 std::string datastring((char*)&data[2], datasize-2);
1286                 std::istringstream is(datastring, std::ios_base::binary);
1287                 
1288                 // Read stuff
1289                 is.read((char*)buf, 2);
1290                 u16 len = readU16(buf);
1291                 
1292                 std::wstring message;
1293                 for(u16 i=0; i<len; i++)
1294                 {
1295                         is.read((char*)buf, 2);
1296                         message += (wchar_t)readU16(buf);
1297                 }
1298
1299                 /*infostream<<"Client received chat message: "
1300                                 <<wide_to_narrow(message)<<std::endl;*/
1301                 
1302                 m_chat_queue.push_back(message);
1303         }
1304         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1305         {
1306                 //if(g_settings->getBool("enable_experimental"))
1307                 {
1308                         /*
1309                                 u16 command
1310                                 u16 count of removed objects
1311                                 for all removed objects {
1312                                         u16 id
1313                                 }
1314                                 u16 count of added objects
1315                                 for all added objects {
1316                                         u16 id
1317                                         u8 type
1318                                         u32 initialization data length
1319                                         string initialization data
1320                                 }
1321                         */
1322
1323                         char buf[6];
1324                         // Get all data except the command number
1325                         std::string datastring((char*)&data[2], datasize-2);
1326                         // Throw them in an istringstream
1327                         std::istringstream is(datastring, std::ios_base::binary);
1328
1329                         // Read stuff
1330                         
1331                         // Read removed objects
1332                         is.read(buf, 2);
1333                         u16 removed_count = readU16((u8*)buf);
1334                         for(u16 i=0; i<removed_count; i++)
1335                         {
1336                                 is.read(buf, 2);
1337                                 u16 id = readU16((u8*)buf);
1338                                 // Remove it
1339                                 {
1340                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1341                                         m_env.removeActiveObject(id);
1342                                 }
1343                         }
1344                         
1345                         // Read added objects
1346                         is.read(buf, 2);
1347                         u16 added_count = readU16((u8*)buf);
1348                         for(u16 i=0; i<added_count; i++)
1349                         {
1350                                 is.read(buf, 2);
1351                                 u16 id = readU16((u8*)buf);
1352                                 is.read(buf, 1);
1353                                 u8 type = readU8((u8*)buf);
1354                                 std::string data = deSerializeLongString(is);
1355                                 // Add it
1356                                 {
1357                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1358                                         m_env.addActiveObject(id, type, data);
1359                                 }
1360                         }
1361                 }
1362         }
1363         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1364         {
1365                 //if(g_settings->getBool("enable_experimental"))
1366                 {
1367                         /*
1368                                 u16 command
1369                                 for all objects
1370                                 {
1371                                         u16 id
1372                                         u16 message length
1373                                         string message
1374                                 }
1375                         */
1376                         char buf[6];
1377                         // Get all data except the command number
1378                         std::string datastring((char*)&data[2], datasize-2);
1379                         // Throw them in an istringstream
1380                         std::istringstream is(datastring, std::ios_base::binary);
1381                         
1382                         while(is.eof() == false)
1383                         {
1384                                 // Read stuff
1385                                 is.read(buf, 2);
1386                                 u16 id = readU16((u8*)buf);
1387                                 if(is.eof())
1388                                         break;
1389                                 is.read(buf, 2);
1390                                 u16 message_size = readU16((u8*)buf);
1391                                 std::string message;
1392                                 message.reserve(message_size);
1393                                 for(u16 i=0; i<message_size; i++)
1394                                 {
1395                                         is.read(buf, 1);
1396                                         message.append(buf, 1);
1397                                 }
1398                                 // Pass on to the environment
1399                                 {
1400                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1401                                         m_env.processActiveObjectMessage(id, message);
1402                                 }
1403                         }
1404                 }
1405         }
1406         else if(command == TOCLIENT_HP)
1407         {
1408                 std::string datastring((char*)&data[2], datasize-2);
1409                 std::istringstream is(datastring, std::ios_base::binary);
1410                 Player *player = m_env.getLocalPlayer();
1411                 assert(player != NULL);
1412                 u8 hp = readU8(is);
1413                 player->hp = hp;
1414         }
1415         else if(command == TOCLIENT_MOVE_PLAYER)
1416         {
1417                 std::string datastring((char*)&data[2], datasize-2);
1418                 std::istringstream is(datastring, std::ios_base::binary);
1419                 Player *player = m_env.getLocalPlayer();
1420                 assert(player != NULL);
1421                 v3f pos = readV3F1000(is);
1422                 f32 pitch = readF1000(is);
1423                 f32 yaw = readF1000(is);
1424                 player->setPosition(pos);
1425                 /*player->setPitch(pitch);
1426                 player->setYaw(yaw);*/
1427
1428                 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1429                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1430                                 <<" pitch="<<pitch
1431                                 <<" yaw="<<yaw
1432                                 <<std::endl;
1433
1434                 /*
1435                         Add to ClientEvent queue.
1436                         This has to be sent to the main program because otherwise
1437                         it would just force the pitch and yaw values to whatever
1438                         the camera points to.
1439                 */
1440                 ClientEvent event;
1441                 event.type = CE_PLAYER_FORCE_MOVE;
1442                 event.player_force_move.pitch = pitch;
1443                 event.player_force_move.yaw = yaw;
1444                 m_client_event_queue.push_back(event);
1445
1446                 // Ignore damage for a few seconds, so that the player doesn't
1447                 // get damage from falling on ground
1448                 m_ignore_damage_timer = 3.0;
1449         }
1450         else if(command == TOCLIENT_PLAYERITEM)
1451         {
1452                 std::string datastring((char*)&data[2], datasize-2);
1453                 std::istringstream is(datastring, std::ios_base::binary);
1454
1455                 u16 count = readU16(is);
1456
1457                 for (u16 i = 0; i < count; ++i) {
1458                         u16 peer_id = readU16(is);
1459                         Player *player = m_env.getPlayer(peer_id);
1460
1461                         if (player == NULL)
1462                         {
1463                                 infostream<<"Client: ignoring player item "
1464                                         << deSerializeString(is)
1465                                         << " for non-existing peer id " << peer_id
1466                                         << std::endl;
1467                                 continue;
1468                         } else if (player->isLocal()) {
1469                                 infostream<<"Client: ignoring player item "
1470                                         << deSerializeString(is)
1471                                         << " for local player" << std::endl;
1472                                 continue;
1473                         } else {
1474                                 InventoryList *inv = player->inventory.getList("main");
1475                                 std::string itemstring(deSerializeString(is));
1476                                 if (itemstring.empty()) {
1477                                         inv->deleteItem(0);
1478                                         infostream
1479                                                 <<"Client: empty player item for peer "
1480                                                 << peer_id << std::endl;
1481                                 } else {
1482                                         std::istringstream iss(itemstring);
1483                                         delete inv->changeItem(0,
1484                                                         InventoryItem::deSerialize(iss, this));
1485                                         infostream<<"Client: player item for peer " << peer_id << ": ";
1486                                         player->getWieldItem()->serialize(infostream);
1487                                         infostream<<std::endl;
1488                                 }
1489                         }
1490                 }
1491         }
1492         else if(command == TOCLIENT_DEATHSCREEN)
1493         {
1494                 std::string datastring((char*)&data[2], datasize-2);
1495                 std::istringstream is(datastring, std::ios_base::binary);
1496                 
1497                 bool set_camera_point_target = readU8(is);
1498                 v3f camera_point_target = readV3F1000(is);
1499                 
1500                 ClientEvent event;
1501                 event.type = CE_DEATHSCREEN;
1502                 event.deathscreen.set_camera_point_target = set_camera_point_target;
1503                 event.deathscreen.camera_point_target_x = camera_point_target.X;
1504                 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1505                 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1506                 m_client_event_queue.push_back(event);
1507         }
1508         else if(command == TOCLIENT_TEXTURES)
1509         {
1510                 io::IFileSystem *irrfs = m_device->getFileSystem();
1511                 video::IVideoDriver *vdrv = m_device->getVideoDriver();
1512
1513                 std::string datastring((char*)&data[2], datasize-2);
1514                 std::istringstream is(datastring, std::ios_base::binary);
1515
1516                 // Stop threads while updating content definitions
1517                 m_mesh_update_thread.stop();
1518                 
1519                 /*
1520                         u16 command
1521                         u16 total number of texture bunches
1522                         u16 index of this bunch
1523                         u32 number of textures in this bunch
1524                         for each texture {
1525                                 u16 length of name
1526                                 string name
1527                                 u32 length of data
1528                                 data
1529                         }
1530                 */
1531                 int num_bunches = readU16(is);
1532                 int bunch_i = readU16(is);
1533                 m_texture_receive_progress = (float)bunch_i / (float)(num_bunches - 1);
1534                 if(bunch_i == num_bunches - 1)
1535                         m_textures_received = true;
1536                 int num_textures = readU32(is);
1537                 infostream<<"Client: Received textures: bunch "<<bunch_i<<"/"
1538                                 <<num_bunches<<" textures="<<num_textures
1539                                 <<" size="<<datasize<<std::endl;
1540                 for(int i=0; i<num_textures; i++){
1541                         std::string name = deSerializeString(is);
1542                         std::string data = deSerializeLongString(is);
1543                         // Silly irrlicht's const-incorrectness
1544                         Buffer<char> data_rw(data.c_str(), data.size());
1545                         // Create an irrlicht memory file
1546                         io::IReadFile *rfile = irrfs->createMemoryReadFile(
1547                                         *data_rw, data.size(), "_tempreadfile");
1548                         assert(rfile);
1549                         // Read image
1550                         video::IImage *img = vdrv->createImageFromFile(rfile);
1551                         if(!img){
1552                                 errorstream<<"Client: Cannot create image from data of "
1553                                                 <<"received texture \""<<name<<"\""<<std::endl;
1554                                 rfile->drop();
1555                                 continue;
1556                         }
1557                         m_tsrc->insertSourceImage(name, img);
1558                         img->drop();
1559                         rfile->drop();
1560                 }
1561                 
1562                 if(m_nodedef_received && m_textures_received){
1563                         // Rebuild inherited images and recreate textures
1564                         m_tsrc->rebuildImagesAndTextures();
1565
1566                         // Update texture atlas
1567                         if(g_settings->getBool("enable_texture_atlas"))
1568                                 m_tsrc->buildMainAtlas(this);
1569                         
1570                         // Update node textures
1571                         m_nodedef->updateTextures(m_tsrc);
1572                 }
1573
1574                 // Resume threads
1575                 m_mesh_update_thread.setRun(true);
1576                 m_mesh_update_thread.Start();
1577
1578                 ClientEvent event;
1579                 event.type = CE_TEXTURES_UPDATED;
1580                 m_client_event_queue.push_back(event);
1581         }
1582         else if(command == TOCLIENT_TOOLDEF)
1583         {
1584                 infostream<<"Client: Received tool definitions: packet size: "
1585                                 <<datasize<<std::endl;
1586
1587                 std::string datastring((char*)&data[2], datasize-2);
1588                 std::istringstream is(datastring, std::ios_base::binary);
1589
1590                 m_tooldef_received = true;
1591
1592                 // Stop threads while updating content definitions
1593                 m_mesh_update_thread.stop();
1594
1595                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1596                 m_tooldef->deSerialize(tmp_is);
1597                 
1598                 // Resume threads
1599                 m_mesh_update_thread.setRun(true);
1600                 m_mesh_update_thread.Start();
1601         }
1602         else if(command == TOCLIENT_NODEDEF)
1603         {
1604                 infostream<<"Client: Received node definitions: packet size: "
1605                                 <<datasize<<std::endl;
1606
1607                 std::string datastring((char*)&data[2], datasize-2);
1608                 std::istringstream is(datastring, std::ios_base::binary);
1609
1610                 m_nodedef_received = true;
1611
1612                 // Stop threads while updating content definitions
1613                 m_mesh_update_thread.stop();
1614
1615                 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1616                 m_nodedef->deSerialize(tmp_is, this);
1617                 
1618                 if(m_textures_received){
1619                         // Update texture atlas
1620                         if(g_settings->getBool("enable_texture_atlas"))
1621                                 m_tsrc->buildMainAtlas(this);
1622                         
1623                         // Update node textures
1624                         m_nodedef->updateTextures(m_tsrc);
1625                 }
1626
1627                 // Resume threads
1628                 m_mesh_update_thread.setRun(true);
1629                 m_mesh_update_thread.Start();
1630         }
1631         else
1632         {
1633                 infostream<<"Client: Ignoring unknown command "
1634                                 <<command<<std::endl;
1635         }
1636 }
1637
1638 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1639 {
1640         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1641         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1642 }
1643
1644 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1645                 v3s16 nodepos_oversurface, u16 item)
1646 {
1647         if(connectedAndInitialized() == false){
1648                 infostream<<"Client::groundAction() "
1649                                 "cancelled (not connected)"
1650                                 <<std::endl;
1651                 return;
1652         }
1653         
1654         /*
1655                 length: 17
1656                 [0] u16 command
1657                 [2] u8 action
1658                 [3] v3s16 nodepos_undersurface
1659                 [9] v3s16 nodepos_abovesurface
1660                 [15] u16 item
1661                 actions:
1662                 0: start digging
1663                 1: place block
1664                 2: stop digging (all parameters ignored)
1665                 3: digging completed
1666         */
1667         u8 datasize = 2 + 1 + 6 + 6 + 2;
1668         SharedBuffer<u8> data(datasize);
1669         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1670         writeU8(&data[2], action);
1671         writeV3S16(&data[3], nodepos_undersurface);
1672         writeV3S16(&data[9], nodepos_oversurface);
1673         writeU16(&data[15], item);
1674         Send(0, data, true);
1675 }
1676
1677 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1678 {
1679         if(connectedAndInitialized() == false){
1680                 infostream<<"Client::clickActiveObject() "
1681                                 "cancelled (not connected)"
1682                                 <<std::endl;
1683                 return;
1684         }
1685
1686         Player *player = m_env.getLocalPlayer();
1687         if(player == NULL)
1688                 return;
1689
1690         ClientActiveObject *obj = m_env.getActiveObject(id);
1691         if(obj){
1692                 if(button == 0){
1693                         ToolItem *titem = NULL;
1694                         std::string toolname = "";
1695
1696                         InventoryList *mlist = player->inventory.getList("main");
1697                         if(mlist != NULL)
1698                         {
1699                                 InventoryItem *item = mlist->getItem(item_i);
1700                                 if(item && (std::string)item->getName() == "ToolItem")
1701                                 {
1702                                         titem = (ToolItem*)item;
1703                                         toolname = titem->getToolName();
1704                                 }
1705                         }
1706
1707                         v3f playerpos = player->getPosition();
1708                         v3f objpos = obj->getPosition();
1709                         v3f dir = (objpos - playerpos).normalize();
1710                         
1711                         bool disable_send = obj->directReportPunch(toolname, dir);
1712                         
1713                         if(disable_send)
1714                                 return;
1715                 }
1716         }
1717         
1718         /*
1719                 length: 7
1720                 [0] u16 command
1721                 [2] u8 button (0=left, 1=right)
1722                 [3] u16 id
1723                 [5] u16 item
1724         */
1725         u8 datasize = 2 + 1 + 6 + 2 + 2;
1726         SharedBuffer<u8> data(datasize);
1727         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1728         writeU8(&data[2], button);
1729         writeU16(&data[3], id);
1730         writeU16(&data[5], item_i);
1731         Send(0, data, true);
1732 }
1733
1734 void Client::sendSignNodeText(v3s16 p, std::string text)
1735 {
1736         /*
1737                 u16 command
1738                 v3s16 p
1739                 u16 textlen
1740                 textdata
1741         */
1742         std::ostringstream os(std::ios_base::binary);
1743         u8 buf[12];
1744         
1745         // Write command
1746         writeU16(buf, TOSERVER_SIGNNODETEXT);
1747         os.write((char*)buf, 2);
1748         
1749         // Write p
1750         writeV3S16(buf, p);
1751         os.write((char*)buf, 6);
1752
1753         u16 textlen = text.size();
1754         // Write text length
1755         writeS16(buf, textlen);
1756         os.write((char*)buf, 2);
1757
1758         // Write text
1759         os.write((char*)text.c_str(), textlen);
1760         
1761         // Make data buffer
1762         std::string s = os.str();
1763         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1764         // Send as reliable
1765         Send(0, data, true);
1766 }
1767         
1768 void Client::sendInventoryAction(InventoryAction *a)
1769 {
1770         std::ostringstream os(std::ios_base::binary);
1771         u8 buf[12];
1772         
1773         // Write command
1774         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1775         os.write((char*)buf, 2);
1776
1777         a->serialize(os);
1778         
1779         // Make data buffer
1780         std::string s = os.str();
1781         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1782         // Send as reliable
1783         Send(0, data, true);
1784 }
1785
1786 void Client::sendChatMessage(const std::wstring &message)
1787 {
1788         std::ostringstream os(std::ios_base::binary);
1789         u8 buf[12];
1790         
1791         // Write command
1792         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1793         os.write((char*)buf, 2);
1794         
1795         // Write length
1796         writeU16(buf, message.size());
1797         os.write((char*)buf, 2);
1798         
1799         // Write string
1800         for(u32 i=0; i<message.size(); i++)
1801         {
1802                 u16 w = message[i];
1803                 writeU16(buf, w);
1804                 os.write((char*)buf, 2);
1805         }
1806         
1807         // Make data buffer
1808         std::string s = os.str();
1809         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1810         // Send as reliable
1811         Send(0, data, true);
1812 }
1813
1814 void Client::sendChangePassword(const std::wstring oldpassword,
1815                 const std::wstring newpassword)
1816 {
1817         Player *player = m_env.getLocalPlayer();
1818         if(player == NULL)
1819                 return;
1820
1821         std::string playername = player->getName();
1822         std::string oldpwd = translatePassword(playername, oldpassword);
1823         std::string newpwd = translatePassword(playername, newpassword);
1824
1825         std::ostringstream os(std::ios_base::binary);
1826         u8 buf[2+PASSWORD_SIZE*2];
1827         /*
1828                 [0] u16 TOSERVER_PASSWORD
1829                 [2] u8[28] old password
1830                 [30] u8[28] new password
1831         */
1832
1833         writeU16(buf, TOSERVER_PASSWORD);
1834         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1835         {
1836                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1837                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1838         }
1839         buf[2+PASSWORD_SIZE-1] = 0;
1840         buf[30+PASSWORD_SIZE-1] = 0;
1841         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1842
1843         // Make data buffer
1844         std::string s = os.str();
1845         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1846         // Send as reliable
1847         Send(0, data, true);
1848 }
1849
1850
1851 void Client::sendDamage(u8 damage)
1852 {
1853         DSTACK(__FUNCTION_NAME);
1854         std::ostringstream os(std::ios_base::binary);
1855
1856         writeU16(os, TOSERVER_DAMAGE);
1857         writeU8(os, damage);
1858
1859         // Make data buffer
1860         std::string s = os.str();
1861         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1862         // Send as reliable
1863         Send(0, data, true);
1864 }
1865
1866 void Client::sendRespawn()
1867 {
1868         DSTACK(__FUNCTION_NAME);
1869         std::ostringstream os(std::ios_base::binary);
1870
1871         writeU16(os, TOSERVER_RESPAWN);
1872
1873         // Make data buffer
1874         std::string s = os.str();
1875         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1876         // Send as reliable
1877         Send(0, data, true);
1878 }
1879
1880 void Client::sendPlayerPos()
1881 {
1882         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1883         
1884         Player *myplayer = m_env.getLocalPlayer();
1885         if(myplayer == NULL)
1886                 return;
1887         
1888         u16 our_peer_id;
1889         {
1890                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1891                 our_peer_id = m_con.GetPeerID();
1892         }
1893         
1894         // Set peer id if not set already
1895         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1896                 myplayer->peer_id = our_peer_id;
1897         // Check that an existing peer_id is the same as the connection's
1898         assert(myplayer->peer_id == our_peer_id);
1899         
1900         v3f pf = myplayer->getPosition();
1901         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1902         v3f sf = myplayer->getSpeed();
1903         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1904         s32 pitch = myplayer->getPitch() * 100;
1905         s32 yaw = myplayer->getYaw() * 100;
1906
1907         /*
1908                 Format:
1909                 [0] u16 command
1910                 [2] v3s32 position*100
1911                 [2+12] v3s32 speed*100
1912                 [2+12+12] s32 pitch*100
1913                 [2+12+12+4] s32 yaw*100
1914         */
1915
1916         SharedBuffer<u8> data(2+12+12+4+4);
1917         writeU16(&data[0], TOSERVER_PLAYERPOS);
1918         writeV3S32(&data[2], position);
1919         writeV3S32(&data[2+12], speed);
1920         writeS32(&data[2+12+12], pitch);
1921         writeS32(&data[2+12+12+4], yaw);
1922
1923         // Send as unreliable
1924         Send(0, data, false);
1925 }
1926
1927 void Client::sendPlayerItem(u16 item)
1928 {
1929         Player *myplayer = m_env.getLocalPlayer();
1930         if(myplayer == NULL)
1931                 return;
1932
1933         u16 our_peer_id = m_con.GetPeerID();
1934
1935         // Set peer id if not set already
1936         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1937                 myplayer->peer_id = our_peer_id;
1938         // Check that an existing peer_id is the same as the connection's
1939         assert(myplayer->peer_id == our_peer_id);
1940
1941         SharedBuffer<u8> data(2+2);
1942         writeU16(&data[0], TOSERVER_PLAYERITEM);
1943         writeU16(&data[2], item);
1944
1945         // Send as reliable
1946         Send(0, data, true);
1947 }
1948
1949 void Client::removeNode(v3s16 p)
1950 {
1951         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1952         
1953         core::map<v3s16, MapBlock*> modified_blocks;
1954
1955         try
1956         {
1957                 //TimeTaker t("removeNodeAndUpdate", m_device);
1958                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1959         }
1960         catch(InvalidPositionException &e)
1961         {
1962         }
1963         
1964         for(core::map<v3s16, MapBlock * >::Iterator
1965                         i = modified_blocks.getIterator();
1966                         i.atEnd() == false; i++)
1967         {
1968                 v3s16 p = i.getNode()->getKey();
1969                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1970                 addUpdateMeshTaskWithEdge(p);
1971         }
1972 }
1973
1974 void Client::addNode(v3s16 p, MapNode n)
1975 {
1976         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1977
1978         TimeTaker timer1("Client::addNode()");
1979
1980         core::map<v3s16, MapBlock*> modified_blocks;
1981
1982         try
1983         {
1984                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1985                 std::string st = std::string("");
1986                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1987         }
1988         catch(InvalidPositionException &e)
1989         {}
1990         
1991         //TimeTaker timer2("Client::addNode(): updateMeshes");
1992
1993         for(core::map<v3s16, MapBlock * >::Iterator
1994                         i = modified_blocks.getIterator();
1995                         i.atEnd() == false; i++)
1996         {
1997                 v3s16 p = i.getNode()->getKey();
1998                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1999                 addUpdateMeshTaskWithEdge(p);
2000         }
2001 }
2002         
2003 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
2004 {
2005         m_env.getClientMap().updateCamera(pos, dir, fov);
2006 }
2007
2008 void Client::renderPostFx()
2009 {
2010         m_env.getClientMap().renderPostFx();
2011 }
2012
2013 MapNode Client::getNode(v3s16 p)
2014 {
2015         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2016         return m_env.getMap().getNode(p);
2017 }
2018
2019 NodeMetadata* Client::getNodeMetadata(v3s16 p)
2020 {
2021         return m_env.getMap().getNodeMetadata(p);
2022 }
2023
2024 LocalPlayer* Client::getLocalPlayer()
2025 {
2026         return m_env.getLocalPlayer();
2027 }
2028
2029 void Client::setPlayerControl(PlayerControl &control)
2030 {
2031         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2032         LocalPlayer *player = m_env.getLocalPlayer();
2033         assert(player != NULL);
2034         player->control = control;
2035 }
2036
2037 void Client::selectPlayerItem(u16 item)
2038 {
2039         LocalPlayer *player = m_env.getLocalPlayer();
2040         assert(player != NULL);
2041
2042         player->wieldItem(item);
2043
2044         sendPlayerItem(item);
2045 }
2046
2047 // Returns true if the inventory of the local player has been
2048 // updated from the server. If it is true, it is set to false.
2049 bool Client::getLocalInventoryUpdated()
2050 {
2051         // m_inventory_updated is behind envlock
2052         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2053         bool updated = m_inventory_updated;
2054         m_inventory_updated = false;
2055         return updated;
2056 }
2057
2058 // Copies the inventory of the local player to parameter
2059 void Client::getLocalInventory(Inventory &dst)
2060 {
2061         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2062         Player *player = m_env.getLocalPlayer();
2063         assert(player != NULL);
2064         dst = player->inventory;
2065 }
2066
2067 InventoryContext *Client::getInventoryContext()
2068 {
2069         return &m_inventory_context;
2070 }
2071
2072 Inventory* Client::getInventory(InventoryContext *c, std::string id)
2073 {
2074         if(id == "current_player")
2075         {
2076                 assert(c->current_player);
2077                 return &(c->current_player->inventory);
2078         }
2079         
2080         Strfnd fn(id);
2081         std::string id0 = fn.next(":");
2082
2083         if(id0 == "nodemeta")
2084         {
2085                 v3s16 p;
2086                 p.X = stoi(fn.next(","));
2087                 p.Y = stoi(fn.next(","));
2088                 p.Z = stoi(fn.next(","));
2089                 NodeMetadata* meta = getNodeMetadata(p);
2090                 if(meta)
2091                         return meta->getInventory();
2092                 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2093                                 <<"no metadata found"<<std::endl;
2094                 return NULL;
2095         }
2096
2097         infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2098         return NULL;
2099 }
2100 void Client::inventoryAction(InventoryAction *a)
2101 {
2102         sendInventoryAction(a);
2103 }
2104
2105 ClientActiveObject * Client::getSelectedActiveObject(
2106                 f32 max_d,
2107                 v3f from_pos_f_on_map,
2108                 core::line3d<f32> shootline_on_map
2109         )
2110 {
2111         core::array<DistanceSortedActiveObject> objects;
2112
2113         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2114
2115         //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2116         
2117         // Sort them.
2118         // After this, the closest object is the first in the array.
2119         objects.sort();
2120
2121         for(u32 i=0; i<objects.size(); i++)
2122         {
2123                 ClientActiveObject *obj = objects[i].obj;
2124                 
2125                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2126                 if(selection_box == NULL)
2127                         continue;
2128
2129                 v3f pos = obj->getPosition();
2130
2131                 core::aabbox3d<f32> offsetted_box(
2132                                 selection_box->MinEdge + pos,
2133                                 selection_box->MaxEdge + pos
2134                 );
2135
2136                 if(offsetted_box.intersectsWithLine(shootline_on_map))
2137                 {
2138                         //infostream<<"Returning selected object"<<std::endl;
2139                         return obj;
2140                 }
2141         }
2142
2143         //infostream<<"No object selected; returning NULL."<<std::endl;
2144         return NULL;
2145 }
2146
2147 void Client::printDebugInfo(std::ostream &os)
2148 {
2149         //JMutexAutoLock lock1(m_fetchblock_mutex);
2150         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2151
2152         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2153                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2154                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2155                 <<std::endl;*/
2156 }
2157         
2158 u32 Client::getDayNightRatio()
2159 {
2160         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2161         return m_env.getDayNightRatio();
2162 }
2163
2164 u16 Client::getHP()
2165 {
2166         Player *player = m_env.getLocalPlayer();
2167         assert(player != NULL);
2168         return player->hp;
2169 }
2170
2171 void Client::setTempMod(v3s16 p, NodeMod mod)
2172 {
2173         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2174         assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2175
2176         core::map<v3s16, MapBlock*> affected_blocks;
2177         ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2178                         &affected_blocks);
2179
2180         for(core::map<v3s16, MapBlock*>::Iterator
2181                         i = affected_blocks.getIterator();
2182                         i.atEnd() == false; i++)
2183         {
2184                 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2185         }
2186 }
2187
2188 void Client::clearTempMod(v3s16 p)
2189 {
2190         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2191         assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2192
2193         core::map<v3s16, MapBlock*> affected_blocks;
2194         ((ClientMap&)m_env.getMap()).clearTempMod(p,
2195                         &affected_blocks);
2196
2197         for(core::map<v3s16, MapBlock*>::Iterator
2198                         i = affected_blocks.getIterator();
2199                         i.atEnd() == false; i++)
2200         {
2201                 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
2202         }
2203 }
2204
2205 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2206 {
2207         /*infostream<<"Client::addUpdateMeshTask(): "
2208                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2209                         <<std::endl;*/
2210
2211         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2212         if(b == NULL)
2213                 return;
2214         
2215         /*
2216                 Create a task to update the mesh of the block
2217         */
2218         
2219         MeshMakeData *data = new MeshMakeData;
2220         
2221         {
2222                 //TimeTaker timer("data fill");
2223                 // Release: ~0ms
2224                 // Debug: 1-6ms, avg=2ms
2225                 data->fill(getDayNightRatio(), b);
2226         }
2227
2228         // Debug wait
2229         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2230         
2231         // Add task to queue
2232         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2233
2234         /*infostream<<"Mesh update input queue size is "
2235                         <<m_mesh_update_thread.m_queue_in.size()
2236                         <<std::endl;*/
2237         
2238 #if 0
2239         // Temporary test: make mesh directly in here
2240         {
2241                 //TimeTaker timer("make mesh");
2242                 // 10ms
2243                 scene::SMesh *mesh_new = NULL;
2244                 mesh_new = makeMapBlockMesh(data);
2245                 b->replaceMesh(mesh_new);
2246                 delete data;
2247         }
2248 #endif
2249
2250         /*
2251                 Mark mesh as non-expired at this point so that it can already
2252                 be marked as expired again if the data changes
2253         */
2254         b->setMeshExpired(false);
2255 }
2256
2257 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2258 {
2259         /*{
2260                 v3s16 p = blockpos;
2261                 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2262                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2263                                 <<std::endl;
2264         }*/
2265
2266         try{
2267                 v3s16 p = blockpos + v3s16(0,0,0);
2268                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2269                 addUpdateMeshTask(p, ack_to_server);
2270         }
2271         catch(InvalidPositionException &e){}
2272         // Leading edge
2273         try{
2274                 v3s16 p = blockpos + v3s16(-1,0,0);
2275                 addUpdateMeshTask(p);
2276         }
2277         catch(InvalidPositionException &e){}
2278         try{
2279                 v3s16 p = blockpos + v3s16(0,-1,0);
2280                 addUpdateMeshTask(p);
2281         }
2282         catch(InvalidPositionException &e){}
2283         try{
2284                 v3s16 p = blockpos + v3s16(0,0,-1);
2285                 addUpdateMeshTask(p);
2286         }
2287         catch(InvalidPositionException &e){}
2288 }
2289
2290 ClientEvent Client::getClientEvent()
2291 {
2292         if(m_client_event_queue.size() == 0)
2293         {
2294                 ClientEvent event;
2295                 event.type = CE_NONE;
2296                 return event;
2297         }
2298         return m_client_event_queue.pop_front();
2299 }
2300
2301 float Client::getRTT(void)
2302 {
2303         try{
2304                 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2305         } catch(con::PeerNotFoundException &e){
2306                 return 1337;
2307         }
2308 }
2309
2310 // IGameDef interface
2311 // Under envlock
2312 IToolDefManager* Client::getToolDefManager()
2313 {
2314         return m_tooldef;
2315 }
2316 INodeDefManager* Client::getNodeDefManager()
2317 {
2318         return m_nodedef;
2319 }
2320 ITextureSource* Client::getTextureSource()
2321 {
2322         return m_tsrc;
2323 }
2324 u16 Client::allocateUnknownNodeId(const std::string &name)
2325 {
2326         errorstream<<"Client::allocateUnknownNodeId(): "
2327                         <<"Client cannot allocate node IDs"<<std::endl;
2328         assert(0);
2329         return CONTENT_IGNORE;
2330 }
2331