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