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