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