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