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