]> git.lizzy.rs Git - minetest.git/blob - src/client.cpp
added new submenu for key assignment
[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
29 void * MeshUpdateThread::Thread()
30 {
31         ThreadStarted();
32
33         DSTACK(__FUNCTION_NAME);
34         
35         BEGIN_DEBUG_EXCEPTION_HANDLER
36
37         while(getRun())
38         {
39                 QueuedMeshUpdate *q = m_queue_in.pop();
40                 if(q == NULL)
41                 {
42                         sleep_ms(50);
43                         continue;
44                 }
45
46                 scene::SMesh *mesh_new = NULL;
47                 mesh_new = makeMapBlockMesh(q->data);
48
49                 MeshUpdateResult r;
50                 r.p = q->p;
51                 r.mesh = mesh_new;
52                 r.ack_block_to_server = q->ack_block_to_server;
53
54                 /*dstream<<"MeshUpdateThread: Processed "
55                                 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
56                                 <<std::endl;*/
57
58                 m_queue_out.push_back(r);
59
60                 delete q;
61         }
62
63         END_DEBUG_EXCEPTION_HANDLER
64
65         return NULL;
66 }
67
68 Client::Client(
69                 IrrlichtDevice *device,
70                 const char *playername,
71                 std::string password,
72                 MapDrawControl &control):
73         m_mesh_update_thread(),
74         m_env(
75                 new ClientMap(this, control,
76                         device->getSceneManager()->getRootSceneNode(),
77                         device->getSceneManager(), 666),
78                 device->getSceneManager()
79         ),
80         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
81         m_device(device),
82         camera_position(0,0,0),
83         camera_direction(0,0,1),
84         m_server_ser_ver(SER_FMT_VER_INVALID),
85         m_inventory_updated(false),
86         m_time_of_day(0),
87         m_map_seed(0),
88         m_password(password),
89         m_access_denied(false)
90 {
91         m_packetcounter_timer = 0.0;
92         m_delete_unused_sectors_timer = 0.0;
93         m_connection_reinit_timer = 0.0;
94         m_avg_rtt_timer = 0.0;
95         m_playerpos_send_timer = 0.0;
96         m_ignore_damage_timer = 0.0;
97
98         //m_env_mutex.Init();
99         //m_con_mutex.Init();
100
101         m_mesh_update_thread.Start();
102
103         /*
104                 Add local player
105         */
106         {
107                 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
108
109                 Player *player = new LocalPlayer();
110
111                 player->updateName(playername);
112
113                 m_env.addPlayer(player);
114                 
115                 // Initialize player in the inventory context
116                 m_inventory_context.current_player = player;
117         }
118 }
119
120 Client::~Client()
121 {
122         {
123                 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
124                 m_con.Disconnect();
125         }
126
127         m_mesh_update_thread.setRun(false);
128         while(m_mesh_update_thread.IsRunning())
129                 sleep_ms(100);
130 }
131
132 void Client::connect(Address address)
133 {
134         DSTACK(__FUNCTION_NAME);
135         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
136         m_con.setTimeoutMs(0);
137         m_con.Connect(address);
138 }
139
140 bool Client::connectedAndInitialized()
141 {
142         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
143
144         if(m_con.Connected() == false)
145                 return false;
146         
147         if(m_server_ser_ver == SER_FMT_VER_INVALID)
148                 return false;
149         
150         return true;
151 }
152
153 void Client::step(float dtime)
154 {
155         DSTACK(__FUNCTION_NAME);
156         
157         // Limit a bit
158         if(dtime > 2.0)
159                 dtime = 2.0;
160         
161         if(m_ignore_damage_timer > dtime)
162                 m_ignore_damage_timer -= dtime;
163         else
164                 m_ignore_damage_timer = 0.0;
165         
166         //dstream<<"Client steps "<<dtime<<std::endl;
167
168         {
169                 //TimeTaker timer("ReceiveAll()", m_device);
170                 // 0ms
171                 ReceiveAll();
172         }
173         
174         {
175                 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
176                 // 0ms
177                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
178                 m_con.RunTimeouts(dtime);
179         }
180
181         /*
182                 Packet counter
183         */
184         {
185                 float &counter = m_packetcounter_timer;
186                 counter -= dtime;
187                 if(counter <= 0.0)
188                 {
189                         counter = 20.0;
190                         
191                         dout_client<<"Client packetcounter (20s):"<<std::endl;
192                         m_packetcounter.print(dout_client);
193                         m_packetcounter.clear();
194                 }
195         }
196
197         {
198                 /*
199                         Delete unused sectors
200
201                         NOTE: This jams the game for a while because deleting sectors
202                               clear caches
203                 */
204                 
205                 float &counter = m_delete_unused_sectors_timer;
206                 counter -= dtime;
207                 if(counter <= 0.0)
208                 {
209                         // 3 minute interval
210                         //counter = 180.0;
211                         counter = 60.0;
212
213                         //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
214
215                         core::list<v3s16> deleted_blocks;
216
217                         float delete_unused_sectors_timeout = 
218                                 g_settings.getFloat("client_delete_unused_sectors_timeout");
219         
220                         // Delete sector blocks
221                         /*u32 num = m_env.getMap().deleteUnusedSectors
222                                         (delete_unused_sectors_timeout,
223                                         true, &deleted_blocks);*/
224                         
225                         // Delete whole sectors
226                         u32 num = m_env.getMap().deleteUnusedSectors
227                                         (delete_unused_sectors_timeout,
228                                         false, &deleted_blocks);
229
230                         if(num > 0)
231                         {
232                                 /*dstream<<DTIME<<"Client: Deleted blocks of "<<num
233                                                 <<" unused sectors"<<std::endl;*/
234                                 dstream<<DTIME<<"Client: Deleted "<<num
235                                                 <<" unused sectors"<<std::endl;
236                                 
237                                 /*
238                                         Send info to server
239                                 */
240
241                                 // Env is locked so con can be locked.
242                                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
243                                 
244                                 core::list<v3s16>::Iterator i = deleted_blocks.begin();
245                                 core::list<v3s16> sendlist;
246                                 for(;;)
247                                 {
248                                         if(sendlist.size() == 255 || i == deleted_blocks.end())
249                                         {
250                                                 if(sendlist.size() == 0)
251                                                         break;
252                                                 /*
253                                                         [0] u16 command
254                                                         [2] u8 count
255                                                         [3] v3s16 pos_0
256                                                         [3+6] v3s16 pos_1
257                                                         ...
258                                                 */
259                                                 u32 replysize = 2+1+6*sendlist.size();
260                                                 SharedBuffer<u8> reply(replysize);
261                                                 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
262                                                 reply[2] = sendlist.size();
263                                                 u32 k = 0;
264                                                 for(core::list<v3s16>::Iterator
265                                                                 j = sendlist.begin();
266                                                                 j != sendlist.end(); j++)
267                                                 {
268                                                         writeV3S16(&reply[2+1+6*k], *j);
269                                                         k++;
270                                                 }
271                                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
272
273                                                 if(i == deleted_blocks.end())
274                                                         break;
275
276                                                 sendlist.clear();
277                                         }
278
279                                         sendlist.push_back(*i);
280                                         i++;
281                                 }
282                         }
283                 }
284         }
285
286         bool connected = connectedAndInitialized();
287
288         if(connected == false)
289         {
290                 float &counter = m_connection_reinit_timer;
291                 counter -= dtime;
292                 if(counter <= 0.0)
293                 {
294                         counter = 2.0;
295
296                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
297                         
298                         Player *myplayer = m_env.getLocalPlayer();
299                         assert(myplayer != NULL);
300         
301                         // Send TOSERVER_INIT
302                         // [0] u16 TOSERVER_INIT
303                         // [2] u8 SER_FMT_VER_HIGHEST
304                         // [3] u8[20] player_name
305                         // [23] u8[28] password
306                         SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
307                         writeU16(&data[0], TOSERVER_INIT);
308                         writeU8(&data[2], SER_FMT_VER_HIGHEST);
309                         memset((char*)&data[3], 0, PLAYERNAME_SIZE);
310                         snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
311                         snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
312
313                         // Send as unreliable
314                         Send(0, data, false);
315                 }
316
317                 // Not connected, return
318                 return;
319         }
320
321         /*
322                 Do stuff if connected
323         */
324         
325         /*
326                 Handle environment
327         */
328         {
329                 // 0ms
330                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
331
332                 // Control local player (0ms)
333                 LocalPlayer *player = m_env.getLocalPlayer();
334                 assert(player != NULL);
335                 player->applyControl(dtime);
336
337                 //TimeTaker envtimer("env step", m_device);
338                 // Step environment
339                 m_env.step(dtime);
340
341                 // Step active blocks
342                 for(core::map<v3s16, bool>::Iterator
343                                 i = m_active_blocks.getIterator();
344                                 i.atEnd() == false; i++)
345                 {
346                         v3s16 p = i.getNode()->getKey();
347
348                         MapBlock *block = NULL;
349                         try
350                         {
351                                 block = m_env.getMap().getBlockNoCreate(p);
352                                 block->stepObjects(dtime, false, m_env.getDayNightRatio());
353                         }
354                         catch(InvalidPositionException &e)
355                         {
356                         }
357                 }
358
359                 /*
360                         Get events
361                 */
362                 for(;;)
363                 {
364                         ClientEnvEvent event = m_env.getClientEvent();
365                         if(event.type == CEE_NONE)
366                         {
367                                 break;
368                         }
369                         else if(event.type == CEE_PLAYER_DAMAGE)
370                         {
371                                 if(m_ignore_damage_timer <= 0)
372                                 {
373                                         u8 damage = event.player_damage.amount;
374                                         sendDamage(damage);
375
376                                         // Add to ClientEvent queue
377                                         ClientEvent event;
378                                         event.type = CE_PLAYER_DAMAGE;
379                                         event.player_damage.amount = damage;
380                                         m_client_event_queue.push_back(event);
381                                 }
382                         }
383                 }
384         }
385         
386         /*
387                 Print some info
388         */
389         {
390                 float &counter = m_avg_rtt_timer;
391                 counter += dtime;
392                 if(counter >= 10)
393                 {
394                         counter = 0.0;
395                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
396                         // connectedAndInitialized() is true, peer exists.
397                         con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER);
398                         dstream<<DTIME<<"Client: avg_rtt="<<peer->avg_rtt<<std::endl;
399                 }
400         }
401
402         /*
403                 Send player position to server
404         */
405         {
406                 float &counter = m_playerpos_send_timer;
407                 counter += dtime;
408                 if(counter >= 0.2)
409                 {
410                         counter = 0.0;
411                         sendPlayerPos();
412                 }
413         }
414
415         /*
416                 Replace updated meshes
417         */
418         {
419                 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
420
421                 //TimeTaker timer("** Processing mesh update result queue");
422                 // 0ms
423                 
424                 /*dstream<<"Mesh update result queue size is "
425                                 <<m_mesh_update_thread.m_queue_out.size()
426                                 <<std::endl;*/
427
428                 while(m_mesh_update_thread.m_queue_out.size() > 0)
429                 {
430                         MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
431                         MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
432                         if(block)
433                         {
434                                 block->replaceMesh(r.mesh);
435                         }
436                         if(r.ack_block_to_server)
437                         {
438                                 /*dstream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
439                                                 <<","<<r.p.Z<<")"<<std::endl;*/
440                                 /*
441                                         Acknowledge block
442                                 */
443                                 /*
444                                         [0] u16 command
445                                         [2] u8 count
446                                         [3] v3s16 pos_0
447                                         [3+6] v3s16 pos_1
448                                         ...
449                                 */
450                                 u32 replysize = 2+1+6;
451                                 SharedBuffer<u8> reply(replysize);
452                                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
453                                 reply[2] = 1;
454                                 writeV3S16(&reply[3], r.p);
455                                 // Send as reliable
456                                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
457                         }
458                 }
459         }
460 }
461
462 // Virtual methods from con::PeerHandler
463 void Client::peerAdded(con::Peer *peer)
464 {
465         derr_client<<"Client::peerAdded(): peer->id="
466                         <<peer->id<<std::endl;
467 }
468 void Client::deletingPeer(con::Peer *peer, bool timeout)
469 {
470         derr_client<<"Client::deletingPeer(): "
471                         "Server Peer is getting deleted "
472                         <<"(timeout="<<timeout<<")"<<std::endl;
473 }
474
475 void Client::ReceiveAll()
476 {
477         DSTACK(__FUNCTION_NAME);
478         for(;;)
479         {
480                 try{
481                         Receive();
482                 }
483                 catch(con::NoIncomingDataException &e)
484                 {
485                         break;
486                 }
487                 catch(con::InvalidIncomingDataException &e)
488                 {
489                         dout_client<<DTIME<<"Client::ReceiveAll(): "
490                                         "InvalidIncomingDataException: what()="
491                                         <<e.what()<<std::endl;
492                 }
493         }
494 }
495
496 void Client::Receive()
497 {
498         DSTACK(__FUNCTION_NAME);
499         u32 data_maxsize = 200000;
500         Buffer<u8> data(data_maxsize);
501         u16 sender_peer_id;
502         u32 datasize;
503         {
504                 //TimeTaker t1("con mutex and receive", m_device);
505                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
506                 datasize = m_con.Receive(sender_peer_id, *data, data_maxsize);
507         }
508         //TimeTaker t1("ProcessData", m_device);
509         ProcessData(*data, datasize, sender_peer_id);
510 }
511
512 /*
513         sender_peer_id given to this shall be quaranteed to be a valid peer
514 */
515 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
516 {
517         DSTACK(__FUNCTION_NAME);
518
519         // Ignore packets that don't even fit a command
520         if(datasize < 2)
521         {
522                 m_packetcounter.add(60000);
523                 return;
524         }
525
526         ToClientCommand command = (ToClientCommand)readU16(&data[0]);
527
528         //dstream<<"Client: received command="<<command<<std::endl;
529         m_packetcounter.add((u16)command);
530         
531         /*
532                 If this check is removed, be sure to change the queue
533                 system to know the ids
534         */
535         if(sender_peer_id != PEER_ID_SERVER)
536         {
537                 dout_client<<DTIME<<"Client::ProcessData(): Discarding data not "
538                                 "coming from server: peer_id="<<sender_peer_id
539                                 <<std::endl;
540                 return;
541         }
542
543         con::Peer *peer;
544         {
545                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
546                 // All data is coming from the server
547                 // PeerNotFoundException is handled by caller.
548                 peer = m_con.GetPeer(PEER_ID_SERVER);
549         }
550
551         u8 ser_version = m_server_ser_ver;
552
553         //dstream<<"Client received command="<<(int)command<<std::endl;
554
555         if(command == TOCLIENT_INIT)
556         {
557                 if(datasize < 3)
558                         return;
559
560                 u8 deployed = data[2];
561
562                 dout_client<<DTIME<<"Client: TOCLIENT_INIT received with "
563                                 "deployed="<<((int)deployed&0xff)<<std::endl;
564
565                 if(deployed < SER_FMT_VER_LOWEST
566                                 || deployed > SER_FMT_VER_HIGHEST)
567                 {
568                         derr_client<<DTIME<<"Client: TOCLIENT_INIT: Server sent "
569                                         <<"unsupported ser_fmt_ver"<<std::endl;
570                         return;
571                 }
572                 
573                 m_server_ser_ver = deployed;
574
575                 // Get player position
576                 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
577                 if(datasize >= 2+1+6)
578                         playerpos_s16 = readV3S16(&data[2+1]);
579                 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
580
581                 { //envlock
582                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
583                         
584                         // Set player position
585                         Player *player = m_env.getLocalPlayer();
586                         assert(player != NULL);
587                         player->setPosition(playerpos_f);
588                 }
589                 
590                 if(datasize >= 2+1+6+8)
591                 {
592                         // Get map seed
593                         m_map_seed = readU64(&data[2+1+6]);
594                         dstream<<"Client: received map seed: "<<m_map_seed<<std::endl;
595                 }
596                 
597                 // Reply to server
598                 u32 replysize = 2;
599                 SharedBuffer<u8> reply(replysize);
600                 writeU16(&reply[0], TOSERVER_INIT2);
601                 // Send as reliable
602                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
603
604                 return;
605         }
606
607         if(command == TOCLIENT_ACCESS_DENIED)
608         {
609                 // The server didn't like our password. Note, this needs
610                 // to be processed even if the serialisation format has
611                 // not been agreed yet, the same as TOCLIENT_INIT.
612                 m_access_denied = true;
613                 return;
614         }
615
616         if(ser_version == SER_FMT_VER_INVALID)
617         {
618                 dout_client<<DTIME<<"WARNING: Client: Server serialization"
619                                 " format invalid or not initialized."
620                                 " Skipping incoming command="<<command<<std::endl;
621                 return;
622         }
623         
624         // Just here to avoid putting the two if's together when
625         // making some copypasta
626         {}
627
628         if(command == TOCLIENT_REMOVENODE)
629         {
630                 if(datasize < 8)
631                         return;
632                 v3s16 p;
633                 p.X = readS16(&data[2]);
634                 p.Y = readS16(&data[4]);
635                 p.Z = readS16(&data[6]);
636                 
637                 //TimeTaker t1("TOCLIENT_REMOVENODE");
638                 
639                 // This will clear the cracking animation after digging
640                 ((ClientMap&)m_env.getMap()).clearTempMod(p);
641
642                 removeNode(p);
643         }
644         else if(command == TOCLIENT_ADDNODE)
645         {
646                 if(datasize < 8 + MapNode::serializedLength(ser_version))
647                         return;
648
649                 v3s16 p;
650                 p.X = readS16(&data[2]);
651                 p.Y = readS16(&data[4]);
652                 p.Z = readS16(&data[6]);
653                 
654                 //TimeTaker t1("TOCLIENT_ADDNODE");
655
656                 MapNode n;
657                 n.deSerialize(&data[8], ser_version);
658                 
659                 addNode(p, n);
660         }
661         else if(command == TOCLIENT_BLOCKDATA)
662         {
663                 // Ignore too small packet
664                 if(datasize < 8)
665                         return;
666                         
667                 v3s16 p;
668                 p.X = readS16(&data[2]);
669                 p.Y = readS16(&data[4]);
670                 p.Z = readS16(&data[6]);
671                 
672                 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
673                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
674                 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
675                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
676                 
677                 std::string datastring((char*)&data[8], datasize-8);
678                 std::istringstream istr(datastring, std::ios_base::binary);
679                 
680                 MapSector *sector;
681                 MapBlock *block;
682                 
683                 { //envlock
684                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
685                         
686                         v2s16 p2d(p.X, p.Z);
687                         sector = m_env.getMap().emergeSector(p2d);
688                         
689                         v2s16 sp = sector->getPos();
690                         if(sp != p2d)
691                         {
692                                 dstream<<"ERROR: Got sector with getPos()="
693                                                 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
694                                                 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
695                         }
696
697                         assert(sp == p2d);
698                         //assert(sector->getPos() == p2d);
699
700                         //TimeTaker timer("MapBlock deSerialize");
701                         // 0ms
702                         
703                         try{
704                                 block = sector->getBlockNoCreate(p.Y);
705                                 /*
706                                         Update an existing block
707                                 */
708                                 //dstream<<"Updating"<<std::endl;
709                                 block->deSerialize(istr, ser_version);
710                                 //block->setChangedFlag();
711                         }
712                         catch(InvalidPositionException &e)
713                         {
714                                 /*
715                                         Create a new block
716                                 */
717                                 //dstream<<"Creating new"<<std::endl;
718                                 block = new MapBlock(&m_env.getMap(), p);
719                                 block->deSerialize(istr, ser_version);
720                                 sector->insertBlock(block);
721                                 //block->setChangedFlag();
722
723                                 //DEBUG
724                                 /*NodeMod mod;
725                                 mod.type = NODEMOD_CHANGECONTENT;
726                                 mod.param = CONTENT_MESE;
727                                 block->setTempMod(v3s16(8,10,8), mod);
728                                 block->setTempMod(v3s16(8,9,8), mod);
729                                 block->setTempMod(v3s16(8,8,8), mod);
730                                 block->setTempMod(v3s16(8,7,8), mod);
731                                 block->setTempMod(v3s16(8,6,8), mod);*/
732 #if 0
733                                 /*
734                                         Add some coulds
735                                         Well, this is a dumb way to do it, they should just
736                                         be drawn as separate objects. But the looks of them
737                                         can be tested this way.
738                                 */
739                                 if(p.Y == 3)
740                                 {
741                                         NodeMod mod;
742                                         mod.type = NODEMOD_CHANGECONTENT;
743                                         mod.param = CONTENT_CLOUD;
744                                         v3s16 p2;
745                                         p2.Y = 8;
746                                         for(p2.X=3; p2.X<=13; p2.X++)
747                                         for(p2.Z=3; p2.Z<=13; p2.Z++)
748                                         {
749                                                 block->setTempMod(p2, mod);
750                                         }
751                                 }
752 #endif
753                         }
754                 } //envlock
755
756 #if 0
757                 /*
758                         Acknowledge block
759                 */
760                 /*
761                         [0] u16 command
762                         [2] u8 count
763                         [3] v3s16 pos_0
764                         [3+6] v3s16 pos_1
765                         ...
766                 */
767                 u32 replysize = 2+1+6;
768                 SharedBuffer<u8> reply(replysize);
769                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
770                 reply[2] = 1;
771                 writeV3S16(&reply[3], p);
772                 // Send as reliable
773                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
774 #endif
775
776                 /*
777                         Update Mesh of this block and blocks at x-, y- and z-.
778                         Environment should not be locked as it interlocks with the
779                         main thread, from which is will want to retrieve textures.
780                 */
781
782                 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
783                 
784                 addUpdateMeshTaskWithEdge(p, true);
785         }
786         else if(command == TOCLIENT_PLAYERPOS)
787         {
788                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
789                                 <<std::endl;
790                 /*u16 our_peer_id;
791                 {
792                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
793                         our_peer_id = m_con.GetPeerID();
794                 }
795                 // Cancel if we don't have a peer id
796                 if(our_peer_id == PEER_ID_INEXISTENT){
797                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
798                                         "we have no peer id"
799                                         <<std::endl;
800                         return;
801                 }*/
802
803                 { //envlock
804                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
805                         
806                         u32 player_size = 2+12+12+4+4;
807                                 
808                         u32 player_count = (datasize-2) / player_size;
809                         u32 start = 2;
810                         for(u32 i=0; i<player_count; i++)
811                         {
812                                 u16 peer_id = readU16(&data[start]);
813
814                                 Player *player = m_env.getPlayer(peer_id);
815
816                                 // Skip if player doesn't exist
817                                 if(player == NULL)
818                                 {
819                                         start += player_size;
820                                         continue;
821                                 }
822
823                                 // Skip if player is local player
824                                 if(player->isLocal())
825                                 {
826                                         start += player_size;
827                                         continue;
828                                 }
829
830                                 v3s32 ps = readV3S32(&data[start+2]);
831                                 v3s32 ss = readV3S32(&data[start+2+12]);
832                                 s32 pitch_i = readS32(&data[start+2+12+12]);
833                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
834                                 /*dstream<<"Client: got "
835                                                 <<"pitch_i="<<pitch_i
836                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
837                                 f32 pitch = (f32)pitch_i / 100.0;
838                                 f32 yaw = (f32)yaw_i / 100.0;
839                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
840                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
841                                 player->setPosition(position);
842                                 player->setSpeed(speed);
843                                 player->setPitch(pitch);
844                                 player->setYaw(yaw);
845
846                                 /*dstream<<"Client: player "<<peer_id
847                                                 <<" pitch="<<pitch
848                                                 <<" yaw="<<yaw<<std::endl;*/
849
850                                 start += player_size;
851                         }
852                 } //envlock
853         }
854         else if(command == TOCLIENT_PLAYERINFO)
855         {
856                 u16 our_peer_id;
857                 {
858                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
859                         our_peer_id = m_con.GetPeerID();
860                 }
861                 // Cancel if we don't have a peer id
862                 if(our_peer_id == PEER_ID_INEXISTENT){
863                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
864                                         "we have no peer id"
865                                         <<std::endl;
866                         return;
867                 }
868                 
869                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
870
871                 { //envlock
872                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
873                         
874                         u32 item_size = 2+PLAYERNAME_SIZE;
875                         u32 player_count = (datasize-2) / item_size;
876                         u32 start = 2;
877                         // peer_ids
878                         core::list<u16> players_alive;
879                         for(u32 i=0; i<player_count; i++)
880                         {
881                                 // Make sure the name ends in '\0'
882                                 data[start+2+20-1] = 0;
883
884                                 u16 peer_id = readU16(&data[start]);
885
886                                 players_alive.push_back(peer_id);
887                                 
888                                 /*dstream<<DTIME<<"peer_id="<<peer_id
889                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
890
891                                 // Don't update the info of the local player
892                                 if(peer_id == our_peer_id)
893                                 {
894                                         start += item_size;
895                                         continue;
896                                 }
897
898                                 Player *player = m_env.getPlayer(peer_id);
899
900                                 // Create a player if it doesn't exist
901                                 if(player == NULL)
902                                 {
903                                         player = new RemotePlayer(
904                                                         m_device->getSceneManager()->getRootSceneNode(),
905                                                         m_device,
906                                                         -1);
907                                         player->peer_id = peer_id;
908                                         m_env.addPlayer(player);
909                                         dout_client<<DTIME<<"Client: Adding new player "
910                                                         <<peer_id<<std::endl;
911                                 }
912                                 
913                                 player->updateName((char*)&data[start+2]);
914
915                                 start += item_size;
916                         }
917                         
918                         /*
919                                 Remove those players from the environment that
920                                 weren't listed by the server.
921                         */
922                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
923                         core::list<Player*> players = m_env.getPlayers();
924                         core::list<Player*>::Iterator ip;
925                         for(ip=players.begin(); ip!=players.end(); ip++)
926                         {
927                                 // Ingore local player
928                                 if((*ip)->isLocal())
929                                         continue;
930                                 
931                                 // Warn about a special case
932                                 if((*ip)->peer_id == 0)
933                                 {
934                                         dstream<<DTIME<<"WARNING: Client: Removing "
935                                                         "dead player with id=0"<<std::endl;
936                                 }
937
938                                 bool is_alive = false;
939                                 core::list<u16>::Iterator i;
940                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
941                                 {
942                                         if((*ip)->peer_id == *i)
943                                         {
944                                                 is_alive = true;
945                                                 break;
946                                         }
947                                 }
948                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
949                                                 <<" is_alive="<<is_alive<<std::endl;*/
950                                 if(is_alive)
951                                         continue;
952                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
953                                                 <<std::endl;
954                                 m_env.removePlayer((*ip)->peer_id);
955                         }
956                 } //envlock
957         }
958         else if(command == TOCLIENT_SECTORMETA)
959         {
960                 /*
961                         [0] u16 command
962                         [2] u8 sector count
963                         [3...] v2s16 pos + sector metadata
964                 */
965                 if(datasize < 3)
966                         return;
967
968                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
969
970                 { //envlock
971                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
972                         
973                         std::string datastring((char*)&data[2], datasize-2);
974                         std::istringstream is(datastring, std::ios_base::binary);
975
976                         u8 buf[4];
977
978                         is.read((char*)buf, 1);
979                         u16 sector_count = readU8(buf);
980                         
981                         //dstream<<"sector_count="<<sector_count<<std::endl;
982
983                         for(u16 i=0; i<sector_count; i++)
984                         {
985                                 // Read position
986                                 is.read((char*)buf, 4);
987                                 v2s16 pos = readV2S16(buf);
988                                 /*dstream<<"Client: deserializing sector at "
989                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
990                                 // Create sector
991                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
992                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
993                         }
994                 } //envlock
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