]> git.lizzy.rs Git - minetest.git/blob - src/client.cpp
876d22e8bed70bd8134c3499f767454576cf05ba
[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 #include "nodemetadata.h"
35
36 /*
37         QueuedMeshUpdate
38 */
39
40 QueuedMeshUpdate::QueuedMeshUpdate():
41         p(-1337,-1337,-1337),
42         data(NULL),
43         ack_block_to_server(false)
44 {
45 }
46
47 QueuedMeshUpdate::~QueuedMeshUpdate()
48 {
49         if(data)
50                 delete data;
51 }
52
53 /*
54         MeshUpdateQueue
55 */
56         
57 MeshUpdateQueue::MeshUpdateQueue()
58 {
59         m_mutex.Init();
60 }
61
62 MeshUpdateQueue::~MeshUpdateQueue()
63 {
64         JMutexAutoLock lock(m_mutex);
65
66         core::list<QueuedMeshUpdate*>::Iterator i;
67         for(i=m_queue.begin(); i!=m_queue.end(); i++)
68         {
69                 QueuedMeshUpdate *q = *i;
70                 delete q;
71         }
72 }
73
74 /*
75         peer_id=0 adds with nobody to send to
76 */
77 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
78 {
79         DSTACK(__FUNCTION_NAME);
80
81         assert(data);
82
83         JMutexAutoLock lock(m_mutex);
84
85         /*
86                 Find if block is already in queue.
87                 If it is, update the data and quit.
88         */
89         core::list<QueuedMeshUpdate*>::Iterator i;
90         for(i=m_queue.begin(); i!=m_queue.end(); i++)
91         {
92                 QueuedMeshUpdate *q = *i;
93                 if(q->p == p)
94                 {
95                         if(q->data)
96                                 delete q->data;
97                         q->data = data;
98                         if(ack_block_to_server)
99                                 q->ack_block_to_server = true;
100                         return;
101                 }
102         }
103         
104         /*
105                 Add the block
106         */
107         QueuedMeshUpdate *q = new QueuedMeshUpdate;
108         q->p = p;
109         q->data = data;
110         q->ack_block_to_server = ack_block_to_server;
111         m_queue.push_back(q);
112 }
113
114 // Returned pointer must be deleted
115 // Returns NULL if queue is empty
116 QueuedMeshUpdate * MeshUpdateQueue::pop()
117 {
118         JMutexAutoLock lock(m_mutex);
119
120         core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
121         if(i == m_queue.end())
122                 return NULL;
123         QueuedMeshUpdate *q = *i;
124         m_queue.erase(i);
125         return q;
126 }
127
128 /*
129         MeshUpdateThread
130 */
131
132 void * MeshUpdateThread::Thread()
133 {
134         ThreadStarted();
135
136         log_register_thread("MeshUpdateThread");
137
138         DSTACK(__FUNCTION_NAME);
139         
140         BEGIN_DEBUG_EXCEPTION_HANDLER
141
142         while(getRun())
143         {
144                 /*// Wait for output queue to flush.
145                 // Allow 2 in queue, this makes less frametime jitter.
146                 // Umm actually, there is no much difference
147                 if(m_queue_out.size() >= 2)
148                 {
149                         sleep_ms(3);
150                         continue;
151                 }*/
152
153                 QueuedMeshUpdate *q = m_queue_in.pop();
154                 if(q == NULL)
155                 {
156                         sleep_ms(3);
157                         continue;
158                 }
159
160                 ScopeProfiler sp(g_profiler, "Client: Mesh making");
161
162                 scene::SMesh *mesh_new = NULL;
163                 mesh_new = makeMapBlockMesh(q->data, m_tsrc);
164
165                 MeshUpdateResult r;
166                 r.p = q->p;
167                 r.mesh = mesh_new;
168                 r.ack_block_to_server = q->ack_block_to_server;
169
170                 /*infostream<<"MeshUpdateThread: Processed "
171                                 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
172                                 <<std::endl;*/
173
174                 m_queue_out.push_back(r);
175
176                 delete q;
177         }
178
179         END_DEBUG_EXCEPTION_HANDLER(errorstream)
180
181         return NULL;
182 }
183
184 Client::Client(
185                 IrrlichtDevice *device,
186                 const char *playername,
187                 std::string password,
188                 MapDrawControl &control,
189                 ITextureSource *tsrc,
190                 IToolDefManager *toolmgr):
191         m_tsrc(tsrc),
192         m_toolmgr(toolmgr),
193         m_mesh_update_thread(tsrc),
194         m_env(
195                 new ClientMap(this, this, control,
196                         device->getSceneManager()->getRootSceneNode(),
197                         device->getSceneManager(), 666),
198                 device->getSceneManager(),
199                 tsrc, this
200         ),
201         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
202         m_device(device),
203         m_server_ser_ver(SER_FMT_VER_INVALID),
204         m_inventory_updated(false),
205         m_time_of_day(0),
206         m_map_seed(0),
207         m_password(password),
208         m_access_denied(false)
209 {
210         m_packetcounter_timer = 0.0;
211         //m_delete_unused_sectors_timer = 0.0;
212         m_connection_reinit_timer = 0.0;
213         m_avg_rtt_timer = 0.0;
214         m_playerpos_send_timer = 0.0;
215         m_ignore_damage_timer = 0.0;
216
217         //m_env_mutex.Init();
218         //m_con_mutex.Init();
219
220         m_mesh_update_thread.Start();
221
222         /*
223                 Add local player
224         */
225         {
226                 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
227
228                 Player *player = new LocalPlayer();
229
230                 player->updateName(playername);
231
232                 m_env.addPlayer(player);
233                 
234                 // Initialize player in the inventory context
235                 m_inventory_context.current_player = player;
236         }
237 }
238
239 Client::~Client()
240 {
241         {
242                 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
243                 m_con.Disconnect();
244         }
245
246         m_mesh_update_thread.setRun(false);
247         while(m_mesh_update_thread.IsRunning())
248                 sleep_ms(100);
249 }
250
251 void Client::connect(Address address)
252 {
253         DSTACK(__FUNCTION_NAME);
254         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
255         m_con.SetTimeoutMs(0);
256         m_con.Connect(address);
257 }
258
259 bool Client::connectedAndInitialized()
260 {
261         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
262
263         if(m_con.Connected() == false)
264                 return false;
265         
266         if(m_server_ser_ver == SER_FMT_VER_INVALID)
267                 return false;
268         
269         return true;
270 }
271
272 void Client::step(float dtime)
273 {
274         DSTACK(__FUNCTION_NAME);
275         
276         // Limit a bit
277         if(dtime > 2.0)
278                 dtime = 2.0;
279         
280         if(m_ignore_damage_timer > dtime)
281                 m_ignore_damage_timer -= dtime;
282         else
283                 m_ignore_damage_timer = 0.0;
284         
285         //infostream<<"Client steps "<<dtime<<std::endl;
286
287         {
288                 //TimeTaker timer("ReceiveAll()", m_device);
289                 // 0ms
290                 ReceiveAll();
291         }
292         
293         {
294                 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
295                 // 0ms
296                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
297                 m_con.RunTimeouts(dtime);
298         }
299
300         /*
301                 Packet counter
302         */
303         {
304                 float &counter = m_packetcounter_timer;
305                 counter -= dtime;
306                 if(counter <= 0.0)
307                 {
308                         counter = 20.0;
309                         
310                         infostream<<"Client packetcounter (20s):"<<std::endl;
311                         m_packetcounter.print(infostream);
312                         m_packetcounter.clear();
313                 }
314         }
315         
316         // Get connection status
317         bool connected = connectedAndInitialized();
318
319 #if 0
320         {
321                 /*
322                         Delete unused sectors
323
324                         NOTE: This jams the game for a while because deleting sectors
325                               clear caches
326                 */
327                 
328                 float &counter = m_delete_unused_sectors_timer;
329                 counter -= dtime;
330                 if(counter <= 0.0)
331                 {
332                         // 3 minute interval
333                         //counter = 180.0;
334                         counter = 60.0;
335
336                         //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
337
338                         core::list<v3s16> deleted_blocks;
339
340                         float delete_unused_sectors_timeout = 
341                                 g_settings->getFloat("client_delete_unused_sectors_timeout");
342         
343                         // Delete sector blocks
344                         /*u32 num = m_env.getMap().unloadUnusedData
345                                         (delete_unused_sectors_timeout,
346                                         true, &deleted_blocks);*/
347                         
348                         // Delete whole sectors
349                         m_env.getMap().unloadUnusedData
350                                         (delete_unused_sectors_timeout,
351                                         &deleted_blocks);
352
353                         if(deleted_blocks.size() > 0)
354                         {
355                                 /*infostream<<"Client: Deleted blocks of "<<num
356                                                 <<" unused sectors"<<std::endl;*/
357                                 /*infostream<<"Client: Deleted "<<num
358                                                 <<" unused sectors"<<std::endl;*/
359                                 
360                                 /*
361                                         Send info to server
362                                 */
363
364                                 // Env is locked so con can be locked.
365                                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
366                                 
367                                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
368                                 core::list<v3s16> sendlist;
369                                 for(;;)
370                                 {
371                                         if(sendlist.size() == 255 || i == deleted_blocks.end())
372                                         {
373                                                 if(sendlist.size() == 0)
374                                                         break;
375                                                 /*
376                                                         [0] u16 command
377                                                         [2] u8 count
378                                                         [3] v3s16 pos_0
379                                                         [3+6] v3s16 pos_1
380                                                         ...
381                                                 */
382                                                 u32 replysize = 2+1+6*sendlist.size();
383                                                 SharedBuffer<u8> reply(replysize);
384                                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
385                                                 reply[2] = sendlist.size();
386                                                 u32 k = 0;
387                                                 for(core::list<v3s16>::Iterator
388                                                                 j = sendlist.begin();
389                                                                 j != sendlist.end(); j++)
390                                                 {
391                                                         writeV3S16(&reply[2+1+6*k], *j);
392                                                         k++;
393                                                 }
394                                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
395
396                                                 if(i == deleted_blocks.end())
397                                                         break;
398
399                                                 sendlist.clear();
400                                         }
401
402                                         sendlist.push_back(*i);
403                                         i++;
404                                 }
405                         }
406                 }
407         }
408 #endif
409
410         if(connected == false)
411         {
412                 float &counter = m_connection_reinit_timer;
413                 counter -= dtime;
414                 if(counter <= 0.0)
415                 {
416                         counter = 2.0;
417
418                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
419                         
420                         Player *myplayer = m_env.getLocalPlayer();
421                         assert(myplayer != NULL);
422         
423                         // Send TOSERVER_INIT
424                         // [0] u16 TOSERVER_INIT
425                         // [2] u8 SER_FMT_VER_HIGHEST
426                         // [3] u8[20] player_name
427                         // [23] u8[28] password (new in some version)
428                         // [51] u16 client network protocol version (new in some version)
429                         SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2);
430                         writeU16(&data[0], TOSERVER_INIT);
431                         writeU8(&data[2], SER_FMT_VER_HIGHEST);
432
433                         memset((char*)&data[3], 0, PLAYERNAME_SIZE);
434                         snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
435
436                         /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
437                                         <<std::endl;*/
438
439                         memset((char*)&data[23], 0, PASSWORD_SIZE);
440                         snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
441                         
442                         // This should be incremented in each version
443                         writeU16(&data[51], 3);
444
445                         // Send as unreliable
446                         Send(0, data, false);
447                 }
448
449                 // Not connected, return
450                 return;
451         }
452
453         /*
454                 Do stuff if connected
455         */
456         
457         /*
458                 Run Map's timers and unload unused data
459         */
460         const float map_timer_and_unload_dtime = 5.25;
461         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
462         {
463                 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
464                 core::list<v3s16> deleted_blocks;
465                 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
466                                 g_settings->getFloat("client_unload_unused_data_timeout"),
467                                 &deleted_blocks);
468                                 
469                 /*if(deleted_blocks.size() > 0)
470                         infostream<<"Client: Unloaded "<<deleted_blocks.size()
471                                         <<" unused blocks"<<std::endl;*/
472                         
473                 /*
474                         Send info to server
475                         NOTE: This loop is intentionally iterated the way it is.
476                 */
477
478                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
479                 core::list<v3s16> sendlist;
480                 for(;;)
481                 {
482                         if(sendlist.size() == 255 || i == deleted_blocks.end())
483                         {
484                                 if(sendlist.size() == 0)
485                                         break;
486                                 /*
487                                         [0] u16 command
488                                         [2] u8 count
489                                         [3] v3s16 pos_0
490                                         [3+6] v3s16 pos_1
491                                         ...
492                                 */
493                                 u32 replysize = 2+1+6*sendlist.size();
494                                 SharedBuffer<u8> reply(replysize);
495                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
496                                 reply[2] = sendlist.size();
497                                 u32 k = 0;
498                                 for(core::list<v3s16>::Iterator
499                                                 j = sendlist.begin();
500                                                 j != sendlist.end(); j++)
501                                 {
502                                         writeV3S16(&reply[2+1+6*k], *j);
503                                         k++;
504                                 }
505                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
506
507                                 if(i == deleted_blocks.end())
508                                         break;
509
510                                 sendlist.clear();
511                         }
512
513                         sendlist.push_back(*i);
514                         i++;
515                 }
516         }
517
518         /*
519                 Handle environment
520         */
521         {
522                 // 0ms
523                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
524
525                 // Control local player (0ms)
526                 LocalPlayer *player = m_env.getLocalPlayer();
527                 assert(player != NULL);
528                 player->applyControl(dtime);
529
530                 //TimeTaker envtimer("env step", m_device);
531                 // Step environment
532                 m_env.step(dtime);
533                 
534                 /*
535                         Get events
536                 */
537                 for(;;)
538                 {
539                         ClientEnvEvent event = m_env.getClientEvent();
540                         if(event.type == CEE_NONE)
541                         {
542                                 break;
543                         }
544                         else if(event.type == CEE_PLAYER_DAMAGE)
545                         {
546                                 if(m_ignore_damage_timer <= 0)
547                                 {
548                                         u8 damage = event.player_damage.amount;
549                                         sendDamage(damage);
550
551                                         // Add to ClientEvent queue
552                                         ClientEvent event;
553                                         event.type = CE_PLAYER_DAMAGE;
554                                         event.player_damage.amount = damage;
555                                         m_client_event_queue.push_back(event);
556                                 }
557                         }
558                 }
559         }
560         
561         /*
562                 Print some info
563         */
564         {
565                 float &counter = m_avg_rtt_timer;
566                 counter += dtime;
567                 if(counter >= 10)
568                 {
569                         counter = 0.0;
570                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
571                         // connectedAndInitialized() is true, peer exists.
572                         float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
573                         infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
574                 }
575         }
576
577         /*
578                 Send player position to server
579         */
580         {
581                 float &counter = m_playerpos_send_timer;
582                 counter += dtime;
583                 if(counter >= 0.2)
584                 {
585                         counter = 0.0;
586                         sendPlayerPos();
587                 }
588         }
589
590         /*
591                 Replace updated meshes
592         */
593         {
594                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
595
596                 //TimeTaker timer("** Processing mesh update result queue");
597                 // 0ms
598                 
599                 /*infostream<<"Mesh update result queue size is "
600                                 <<m_mesh_update_thread.m_queue_out.size()
601                                 <<std::endl;*/
602
603                 while(m_mesh_update_thread.m_queue_out.size() > 0)
604                 {
605                         MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
606                         MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
607                         if(block)
608                         {
609                                 block->replaceMesh(r.mesh);
610                         }
611                         if(r.ack_block_to_server)
612                         {
613                                 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
614                                                 <<","<<r.p.Z<<")"<<std::endl;*/
615                                 /*
616                                         Acknowledge block
617                                 */
618                                 /*
619                                         [0] u16 command
620                                         [2] u8 count
621                                         [3] v3s16 pos_0
622                                         [3+6] v3s16 pos_1
623                                         ...
624                                 */
625                                 u32 replysize = 2+1+6;
626                                 SharedBuffer<u8> reply(replysize);
627                                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
628                                 reply[2] = 1;
629                                 writeV3S16(&reply[3], r.p);
630                                 // Send as reliable
631                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
632                         }
633                 }
634         }
635 }
636
637 // Virtual methods from con::PeerHandler
638 void Client::peerAdded(con::Peer *peer)
639 {
640         infostream<<"Client::peerAdded(): peer->id="
641                         <<peer->id<<std::endl;
642 }
643 void Client::deletingPeer(con::Peer *peer, bool timeout)
644 {
645         infostream<<"Client::deletingPeer(): "
646                         "Server Peer is getting deleted "
647                         <<"(timeout="<<timeout<<")"<<std::endl;
648 }
649
650 void Client::ReceiveAll()
651 {
652         DSTACK(__FUNCTION_NAME);
653         for(;;)
654         {
655                 try{
656                         Receive();
657                 }
658                 catch(con::NoIncomingDataException &e)
659                 {
660                         break;
661                 }
662                 catch(con::InvalidIncomingDataException &e)
663                 {
664                         infostream<<"Client::ReceiveAll(): "
665                                         "InvalidIncomingDataException: what()="
666                                         <<e.what()<<std::endl;
667                 }
668         }
669 }
670
671 void Client::Receive()
672 {
673         DSTACK(__FUNCTION_NAME);
674         SharedBuffer<u8> data;
675         u16 sender_peer_id;
676         u32 datasize;
677         {
678                 //TimeTaker t1("con mutex and receive", m_device);
679                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
680                 datasize = m_con.Receive(sender_peer_id, data);
681         }
682         //TimeTaker t1("ProcessData", m_device);
683         ProcessData(*data, datasize, sender_peer_id);
684 }
685
686 /*
687         sender_peer_id given to this shall be quaranteed to be a valid peer
688 */
689 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
690 {
691         DSTACK(__FUNCTION_NAME);
692
693         // Ignore packets that don't even fit a command
694         if(datasize < 2)
695         {
696                 m_packetcounter.add(60000);
697                 return;
698         }
699
700         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
701
702         //infostream<<"Client: received command="<<command<<std::endl;
703         m_packetcounter.add((u16)command);
704         
705         /*
706                 If this check is removed, be sure to change the queue
707                 system to know the ids
708         */
709         if(sender_peer_id != PEER_ID_SERVER)
710         {
711                 infostream<<"Client::ProcessData(): Discarding data not "
712                                 "coming from server: peer_id="<<sender_peer_id
713                                 <<std::endl;
714                 return;
715         }
716
717         u8 ser_version = m_server_ser_ver;
718
719         //infostream<<"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                 infostream<<"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                         infostream<<"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                         infostream<<"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                 infostream<<"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                 /*infostream<<"Client: Thread: BLOCKDATA for ("
846                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
847                 /*infostream<<"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                         //infostream<<"Updating"<<std::endl;
871                         block->deSerialize(istr, ser_version, this);
872                 }
873                 else
874                 {
875                         /*
876                                 Create a new block
877                         */
878                         //infostream<<"Creating new"<<std::endl;
879                         block = new MapBlock(&m_env.getMap(), p);
880                         block->deSerialize(istr, ser_version, this);
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                 //infostream<<"Adding mesh update task for received block"<<std::endl;
925                 addUpdateMeshTaskWithEdge(p, true);
926         }
927         else if(command == TOCLIENT_PLAYERPOS)
928         {
929                 infostream<<"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                         infostream<<"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                                 /*infostream<<"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                                 /*infostream<<"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                         infostream<<"TOCLIENT_PLAYERINFO cancelled: "
1005                                         "we have no peer id"
1006                                         <<std::endl;
1007                         return;
1008                 }
1009                 
1010                 //infostream<<"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                                 /*infostream<<"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                                         infostream<<"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                         //infostream<<"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                                         infostream<<"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                                 /*infostream<<"peer_id="<<((*ip)->peer_id)
1090                                                 <<" is_alive="<<is_alive<<std::endl;*/
1091                                 if(is_alive)
1092                                         continue;
1093                                 infostream<<"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                 infostream<<"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                 //infostream<<"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                         //infostream<<"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                                 /*infostream<<"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(infostream);
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, this);
1166                         //t1.stop();
1167
1168                         m_inventory_updated = true;
1169
1170                         //infostream<<"Client got player inventory:"<<std::endl;
1171                         //player->inventory.print(infostream);
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                         infostream<<"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                 //infostream<<"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                         infostream<<"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                 /*infostream<<"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                 infostream<<"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                                 infostream<<"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                                 infostream<<"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                                         infostream
1468                                                 <<"Client: empty player item for peer "
1469                                                 << peer_id << std::endl;
1470                                 } else {
1471                                         std::istringstream iss(itemstring);
1472                                         delete inv->changeItem(0,
1473                                                         InventoryItem::deSerialize(iss, this));
1474                                         infostream<<"Client: player item for peer " << peer_id << ": ";
1475                                         player->getWieldItem()->serialize(infostream);
1476                                         infostream<<std::endl;
1477                                 }
1478                         }
1479                 }
1480         }
1481         else if(command == TOCLIENT_DEATHSCREEN)
1482         {
1483                 std::string datastring((char*)&data[2], datasize-2);
1484                 std::istringstream is(datastring, std::ios_base::binary);
1485                 
1486                 bool set_camera_point_target = readU8(is);
1487                 v3f camera_point_target = readV3F1000(is);
1488                 
1489                 ClientEvent event;
1490                 event.type = CE_DEATHSCREEN;
1491                 event.deathscreen.set_camera_point_target = set_camera_point_target;
1492                 event.deathscreen.camera_point_target_x = camera_point_target.X;
1493                 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1494                 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1495                 m_client_event_queue.push_back(event);
1496         }
1497         else
1498         {
1499                 infostream<<"Client: Ignoring unknown command "
1500                                 <<command<<std::endl;
1501         }
1502 }
1503
1504 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1505 {
1506         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1507         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1508 }
1509
1510 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1511                 v3s16 nodepos_oversurface, u16 item)
1512 {
1513         if(connectedAndInitialized() == false){
1514                 infostream<<"Client::groundAction() "
1515                                 "cancelled (not connected)"
1516                                 <<std::endl;
1517                 return;
1518         }
1519         
1520         /*
1521                 length: 17
1522                 [0] u16 command
1523                 [2] u8 action
1524                 [3] v3s16 nodepos_undersurface
1525                 [9] v3s16 nodepos_abovesurface
1526                 [15] u16 item
1527                 actions:
1528                 0: start digging
1529                 1: place block
1530                 2: stop digging (all parameters ignored)
1531                 3: digging completed
1532         */
1533         u8 datasize = 2 + 1 + 6 + 6 + 2;
1534         SharedBuffer<u8> data(datasize);
1535         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1536         writeU8(&data[2], action);
1537         writeV3S16(&data[3], nodepos_undersurface);
1538         writeV3S16(&data[9], nodepos_oversurface);
1539         writeU16(&data[15], item);
1540         Send(0, data, true);
1541 }
1542
1543 void Client::clickActiveObject(u8 button, u16 id, u16 item_i)
1544 {
1545         if(connectedAndInitialized() == false){
1546                 infostream<<"Client::clickActiveObject() "
1547                                 "cancelled (not connected)"
1548                                 <<std::endl;
1549                 return;
1550         }
1551
1552         Player *player = m_env.getLocalPlayer();
1553         if(player == NULL)
1554                 return;
1555
1556         ClientActiveObject *obj = m_env.getActiveObject(id);
1557         if(obj){
1558                 if(button == 0){
1559                         ToolItem *titem = NULL;
1560                         std::string toolname = "";
1561
1562                         InventoryList *mlist = player->inventory.getList("main");
1563                         if(mlist != NULL)
1564                         {
1565                                 InventoryItem *item = mlist->getItem(item_i);
1566                                 if(item && (std::string)item->getName() == "ToolItem")
1567                                 {
1568                                         titem = (ToolItem*)item;
1569                                         toolname = titem->getToolName();
1570                                 }
1571                         }
1572
1573                         v3f playerpos = player->getPosition();
1574                         v3f objpos = obj->getPosition();
1575                         v3f dir = (objpos - playerpos).normalize();
1576                         
1577                         bool disable_send = obj->directReportPunch(toolname, dir);
1578                         
1579                         if(disable_send)
1580                                 return;
1581                 }
1582         }
1583         
1584         /*
1585                 length: 7
1586                 [0] u16 command
1587                 [2] u8 button (0=left, 1=right)
1588                 [3] u16 id
1589                 [5] u16 item
1590         */
1591         u8 datasize = 2 + 1 + 6 + 2 + 2;
1592         SharedBuffer<u8> data(datasize);
1593         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1594         writeU8(&data[2], button);
1595         writeU16(&data[3], id);
1596         writeU16(&data[5], item_i);
1597         Send(0, data, true);
1598 }
1599
1600 void Client::sendSignNodeText(v3s16 p, std::string text)
1601 {
1602         /*
1603                 u16 command
1604                 v3s16 p
1605                 u16 textlen
1606                 textdata
1607         */
1608         std::ostringstream os(std::ios_base::binary);
1609         u8 buf[12];
1610         
1611         // Write command
1612         writeU16(buf, TOSERVER_SIGNNODETEXT);
1613         os.write((char*)buf, 2);
1614         
1615         // Write p
1616         writeV3S16(buf, p);
1617         os.write((char*)buf, 6);
1618
1619         u16 textlen = text.size();
1620         // Write text length
1621         writeS16(buf, textlen);
1622         os.write((char*)buf, 2);
1623
1624         // Write text
1625         os.write((char*)text.c_str(), textlen);
1626         
1627         // Make data buffer
1628         std::string s = os.str();
1629         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1630         // Send as reliable
1631         Send(0, data, true);
1632 }
1633         
1634 void Client::sendInventoryAction(InventoryAction *a)
1635 {
1636         std::ostringstream os(std::ios_base::binary);
1637         u8 buf[12];
1638         
1639         // Write command
1640         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1641         os.write((char*)buf, 2);
1642
1643         a->serialize(os);
1644         
1645         // Make data buffer
1646         std::string s = os.str();
1647         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1648         // Send as reliable
1649         Send(0, data, true);
1650 }
1651
1652 void Client::sendChatMessage(const std::wstring &message)
1653 {
1654         std::ostringstream os(std::ios_base::binary);
1655         u8 buf[12];
1656         
1657         // Write command
1658         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1659         os.write((char*)buf, 2);
1660         
1661         // Write length
1662         writeU16(buf, message.size());
1663         os.write((char*)buf, 2);
1664         
1665         // Write string
1666         for(u32 i=0; i<message.size(); i++)
1667         {
1668                 u16 w = message[i];
1669                 writeU16(buf, w);
1670                 os.write((char*)buf, 2);
1671         }
1672         
1673         // Make data buffer
1674         std::string s = os.str();
1675         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1676         // Send as reliable
1677         Send(0, data, true);
1678 }
1679
1680 void Client::sendChangePassword(const std::wstring oldpassword,
1681                 const std::wstring newpassword)
1682 {
1683         Player *player = m_env.getLocalPlayer();
1684         if(player == NULL)
1685                 return;
1686
1687         std::string playername = player->getName();
1688         std::string oldpwd = translatePassword(playername, oldpassword);
1689         std::string newpwd = translatePassword(playername, newpassword);
1690
1691         std::ostringstream os(std::ios_base::binary);
1692         u8 buf[2+PASSWORD_SIZE*2];
1693         /*
1694                 [0] u16 TOSERVER_PASSWORD
1695                 [2] u8[28] old password
1696                 [30] u8[28] new password
1697         */
1698
1699         writeU16(buf, TOSERVER_PASSWORD);
1700         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1701         {
1702                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1703                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1704         }
1705         buf[2+PASSWORD_SIZE-1] = 0;
1706         buf[30+PASSWORD_SIZE-1] = 0;
1707         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1708
1709         // Make data buffer
1710         std::string s = os.str();
1711         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1712         // Send as reliable
1713         Send(0, data, true);
1714 }
1715
1716
1717 void Client::sendDamage(u8 damage)
1718 {
1719         DSTACK(__FUNCTION_NAME);
1720         std::ostringstream os(std::ios_base::binary);
1721
1722         writeU16(os, TOSERVER_DAMAGE);
1723         writeU8(os, damage);
1724
1725         // Make data buffer
1726         std::string s = os.str();
1727         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1728         // Send as reliable
1729         Send(0, data, true);
1730 }
1731
1732 void Client::sendRespawn()
1733 {
1734         DSTACK(__FUNCTION_NAME);
1735         std::ostringstream os(std::ios_base::binary);
1736
1737         writeU16(os, TOSERVER_RESPAWN);
1738
1739         // Make data buffer
1740         std::string s = os.str();
1741         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1742         // Send as reliable
1743         Send(0, data, true);
1744 }
1745
1746 void Client::sendPlayerPos()
1747 {
1748         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1749         
1750         Player *myplayer = m_env.getLocalPlayer();
1751         if(myplayer == NULL)
1752                 return;
1753         
1754         u16 our_peer_id;
1755         {
1756                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1757                 our_peer_id = m_con.GetPeerID();
1758         }
1759         
1760         // Set peer id if not set already
1761         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1762                 myplayer->peer_id = our_peer_id;
1763         // Check that an existing peer_id is the same as the connection's
1764         assert(myplayer->peer_id == our_peer_id);
1765         
1766         v3f pf = myplayer->getPosition();
1767         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1768         v3f sf = myplayer->getSpeed();
1769         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1770         s32 pitch = myplayer->getPitch() * 100;
1771         s32 yaw = myplayer->getYaw() * 100;
1772
1773         /*
1774                 Format:
1775                 [0] u16 command
1776                 [2] v3s32 position*100
1777                 [2+12] v3s32 speed*100
1778                 [2+12+12] s32 pitch*100
1779                 [2+12+12+4] s32 yaw*100
1780         */
1781
1782         SharedBuffer<u8> data(2+12+12+4+4);
1783         writeU16(&data[0], TOSERVER_PLAYERPOS);
1784         writeV3S32(&data[2], position);
1785         writeV3S32(&data[2+12], speed);
1786         writeS32(&data[2+12+12], pitch);
1787         writeS32(&data[2+12+12+4], yaw);
1788
1789         // Send as unreliable
1790         Send(0, data, false);
1791 }
1792
1793 void Client::sendPlayerItem(u16 item)
1794 {
1795         Player *myplayer = m_env.getLocalPlayer();
1796         if(myplayer == NULL)
1797                 return;
1798
1799         u16 our_peer_id = m_con.GetPeerID();
1800
1801         // Set peer id if not set already
1802         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1803                 myplayer->peer_id = our_peer_id;
1804         // Check that an existing peer_id is the same as the connection's
1805         assert(myplayer->peer_id == our_peer_id);
1806
1807         SharedBuffer<u8> data(2+2);
1808         writeU16(&data[0], TOSERVER_PLAYERITEM);
1809         writeU16(&data[2], item);
1810
1811         // Send as reliable
1812         Send(0, data, true);
1813 }
1814
1815 void Client::removeNode(v3s16 p)
1816 {
1817         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1818         
1819         core::map<v3s16, MapBlock*> modified_blocks;
1820
1821         try
1822         {
1823                 //TimeTaker t("removeNodeAndUpdate", m_device);
1824                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1825         }
1826         catch(InvalidPositionException &e)
1827         {
1828         }
1829         
1830         for(core::map<v3s16, MapBlock * >::Iterator
1831                         i = modified_blocks.getIterator();
1832                         i.atEnd() == false; i++)
1833         {
1834                 v3s16 p = i.getNode()->getKey();
1835                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1836                 addUpdateMeshTaskWithEdge(p);
1837         }
1838 }
1839
1840 void Client::addNode(v3s16 p, MapNode n)
1841 {
1842         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1843
1844         TimeTaker timer1("Client::addNode()");
1845
1846         core::map<v3s16, MapBlock*> modified_blocks;
1847
1848         try
1849         {
1850                 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1851                 std::string st = std::string("");
1852                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
1853         }
1854         catch(InvalidPositionException &e)
1855         {}
1856         
1857         //TimeTaker timer2("Client::addNode(): updateMeshes");
1858
1859         for(core::map<v3s16, MapBlock * >::Iterator
1860                         i = modified_blocks.getIterator();
1861                         i.atEnd() == false; i++)
1862         {
1863                 v3s16 p = i.getNode()->getKey();
1864                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1865                 addUpdateMeshTaskWithEdge(p);
1866         }
1867 }
1868         
1869 void Client::updateCamera(v3f pos, v3f dir, f32 fov)
1870 {
1871         m_env.getClientMap().updateCamera(pos, dir, fov);
1872 }
1873
1874 void Client::renderPostFx()
1875 {
1876         m_env.getClientMap().renderPostFx();
1877 }
1878
1879 MapNode Client::getNode(v3s16 p)
1880 {
1881         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1882         return m_env.getMap().getNode(p);
1883 }
1884
1885 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1886 {
1887         return m_env.getMap().getNodeMetadata(p);
1888 }
1889
1890 LocalPlayer* Client::getLocalPlayer()
1891 {
1892         return m_env.getLocalPlayer();
1893 }
1894
1895 void Client::setPlayerControl(PlayerControl &control)
1896 {
1897         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1898         LocalPlayer *player = m_env.getLocalPlayer();
1899         assert(player != NULL);
1900         player->control = control;
1901 }
1902
1903 void Client::selectPlayerItem(u16 item)
1904 {
1905         LocalPlayer *player = m_env.getLocalPlayer();
1906         assert(player != NULL);
1907
1908         player->wieldItem(item);
1909
1910         sendPlayerItem(item);
1911 }
1912
1913 // Returns true if the inventory of the local player has been
1914 // updated from the server. If it is true, it is set to false.
1915 bool Client::getLocalInventoryUpdated()
1916 {
1917         // m_inventory_updated is behind envlock
1918         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1919         bool updated = m_inventory_updated;
1920         m_inventory_updated = false;
1921         return updated;
1922 }
1923
1924 // Copies the inventory of the local player to parameter
1925 void Client::getLocalInventory(Inventory &dst)
1926 {
1927         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1928         Player *player = m_env.getLocalPlayer();
1929         assert(player != NULL);
1930         dst = player->inventory;
1931 }
1932
1933 InventoryContext *Client::getInventoryContext()
1934 {
1935         return &m_inventory_context;
1936 }
1937
1938 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1939 {
1940         if(id == "current_player")
1941         {
1942                 assert(c->current_player);
1943                 return &(c->current_player->inventory);
1944         }
1945         
1946         Strfnd fn(id);
1947         std::string id0 = fn.next(":");
1948
1949         if(id0 == "nodemeta")
1950         {
1951                 v3s16 p;
1952                 p.X = stoi(fn.next(","));
1953                 p.Y = stoi(fn.next(","));
1954                 p.Z = stoi(fn.next(","));
1955                 NodeMetadata* meta = getNodeMetadata(p);
1956                 if(meta)
1957                         return meta->getInventory();
1958                 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1959                                 <<"no metadata found"<<std::endl;
1960                 return NULL;
1961         }
1962
1963         infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1964         return NULL;
1965 }
1966 void Client::inventoryAction(InventoryAction *a)
1967 {
1968         sendInventoryAction(a);
1969 }
1970
1971 ClientActiveObject * Client::getSelectedActiveObject(
1972                 f32 max_d,
1973                 v3f from_pos_f_on_map,
1974                 core::line3d<f32> shootline_on_map
1975         )
1976 {
1977         core::array<DistanceSortedActiveObject> objects;
1978
1979         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1980
1981         //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1982         
1983         // Sort them.
1984         // After this, the closest object is the first in the array.
1985         objects.sort();
1986
1987         for(u32 i=0; i<objects.size(); i++)
1988         {
1989                 ClientActiveObject *obj = objects[i].obj;
1990                 
1991                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1992                 if(selection_box == NULL)
1993                         continue;
1994
1995                 v3f pos = obj->getPosition();
1996
1997                 core::aabbox3d<f32> offsetted_box(
1998                                 selection_box->MinEdge + pos,
1999                                 selection_box->MaxEdge + pos
2000                 );
2001
2002                 if(offsetted_box.intersectsWithLine(shootline_on_map))
2003                 {
2004                         //infostream<<"Returning selected object"<<std::endl;
2005                         return obj;
2006                 }
2007         }
2008
2009         //infostream<<"No object selected; returning NULL."<<std::endl;
2010         return NULL;
2011 }
2012
2013 void Client::printDebugInfo(std::ostream &os)
2014 {
2015         //JMutexAutoLock lock1(m_fetchblock_mutex);
2016         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2017
2018         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2019                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2020                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2021                 <<std::endl;*/
2022 }
2023         
2024 u32 Client::getDayNightRatio()
2025 {
2026         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2027         return m_env.getDayNightRatio();
2028 }
2029
2030 u16 Client::getHP()
2031 {
2032         Player *player = m_env.getLocalPlayer();
2033         assert(player != NULL);
2034         return player->hp;
2035 }
2036
2037 void Client::setTempMod(v3s16 p, NodeMod mod)
2038 {
2039         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2040         assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2041
2042         core::map<v3s16, MapBlock*> affected_blocks;
2043         ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
2044                         &affected_blocks);
2045
2046         for(core::map<v3s16, MapBlock*>::Iterator
2047                         i = affected_blocks.getIterator();
2048                         i.atEnd() == false; i++)
2049         {
2050                 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio(), m_tsrc);
2051         }
2052 }
2053
2054 void Client::clearTempMod(v3s16 p)
2055 {
2056         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2057         assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
2058
2059         core::map<v3s16, MapBlock*> affected_blocks;
2060         ((ClientMap&)m_env.getMap()).clearTempMod(p,
2061                         &affected_blocks);
2062
2063         for(core::map<v3s16, MapBlock*>::Iterator
2064                         i = affected_blocks.getIterator();
2065                         i.atEnd() == false; i++)
2066         {
2067                 i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio(), m_tsrc);
2068         }
2069 }
2070
2071 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
2072 {
2073         /*infostream<<"Client::addUpdateMeshTask(): "
2074                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2075                         <<std::endl;*/
2076
2077         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2078         if(b == NULL)
2079                 return;
2080         
2081         /*
2082                 Create a task to update the mesh of the block
2083         */
2084         
2085         MeshMakeData *data = new MeshMakeData;
2086         
2087         {
2088                 //TimeTaker timer("data fill");
2089                 // Release: ~0ms
2090                 // Debug: 1-6ms, avg=2ms
2091                 data->fill(getDayNightRatio(), b);
2092         }
2093
2094         // Debug wait
2095         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2096         
2097         // Add task to queue
2098         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2099
2100         /*infostream<<"Mesh update input queue size is "
2101                         <<m_mesh_update_thread.m_queue_in.size()
2102                         <<std::endl;*/
2103         
2104 #if 0
2105         // Temporary test: make mesh directly in here
2106         {
2107                 //TimeTaker timer("make mesh");
2108                 // 10ms
2109                 scene::SMesh *mesh_new = NULL;
2110                 mesh_new = makeMapBlockMesh(data);
2111                 b->replaceMesh(mesh_new);
2112                 delete data;
2113         }
2114 #endif
2115
2116         /*
2117                 Mark mesh as non-expired at this point so that it can already
2118                 be marked as expired again if the data changes
2119         */
2120         b->setMeshExpired(false);
2121 }
2122
2123 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2124 {
2125         /*{
2126                 v3s16 p = blockpos;
2127                 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2128                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2129                                 <<std::endl;
2130         }*/
2131
2132         try{
2133                 v3s16 p = blockpos + v3s16(0,0,0);
2134                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2135                 addUpdateMeshTask(p, ack_to_server);
2136         }
2137         catch(InvalidPositionException &e){}
2138         // Leading edge
2139         try{
2140                 v3s16 p = blockpos + v3s16(-1,0,0);
2141                 addUpdateMeshTask(p);
2142         }
2143         catch(InvalidPositionException &e){}
2144         try{
2145                 v3s16 p = blockpos + v3s16(0,-1,0);
2146                 addUpdateMeshTask(p);
2147         }
2148         catch(InvalidPositionException &e){}
2149         try{
2150                 v3s16 p = blockpos + v3s16(0,0,-1);
2151                 addUpdateMeshTask(p);
2152         }
2153         catch(InvalidPositionException &e){}
2154 }
2155
2156 ClientEvent Client::getClientEvent()
2157 {
2158         if(m_client_event_queue.size() == 0)
2159         {
2160                 ClientEvent event;
2161                 event.type = CE_NONE;
2162                 return event;
2163         }
2164         return m_client_event_queue.pop_front();
2165 }
2166
2167 float Client::getRTT(void)
2168 {
2169         try{
2170                 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2171         } catch(con::PeerNotFoundException &e){
2172                 return 1337;
2173         }
2174 }
2175