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