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