]> git.lizzy.rs Git - minetest.git/blob - src/client.cpp
player passwords and privileges in world/auth.txt
[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                 m_access_denied_reason = L"Unknown";
614                 if(datasize >= 4)
615                 {
616                         std::string datastring((char*)&data[2], datasize-2);
617                         std::istringstream is(datastring, std::ios_base::binary);
618                         m_access_denied_reason = deSerializeWideString(is);
619                 }
620                 return;
621         }
622
623         if(ser_version == SER_FMT_VER_INVALID)
624         {
625                 dout_client<<DTIME<<"WARNING: Client: Server serialization"
626                                 " format invalid or not initialized."
627                                 " Skipping incoming command="<<command<<std::endl;
628                 return;
629         }
630         
631         // Just here to avoid putting the two if's together when
632         // making some copypasta
633         {}
634
635         if(command == TOCLIENT_REMOVENODE)
636         {
637                 if(datasize < 8)
638                         return;
639                 v3s16 p;
640                 p.X = readS16(&data[2]);
641                 p.Y = readS16(&data[4]);
642                 p.Z = readS16(&data[6]);
643                 
644                 //TimeTaker t1("TOCLIENT_REMOVENODE");
645                 
646                 // This will clear the cracking animation after digging
647                 ((ClientMap&)m_env.getMap()).clearTempMod(p);
648
649                 removeNode(p);
650         }
651         else if(command == TOCLIENT_ADDNODE)
652         {
653                 if(datasize < 8 + MapNode::serializedLength(ser_version))
654                         return;
655
656                 v3s16 p;
657                 p.X = readS16(&data[2]);
658                 p.Y = readS16(&data[4]);
659                 p.Z = readS16(&data[6]);
660                 
661                 //TimeTaker t1("TOCLIENT_ADDNODE");
662
663                 MapNode n;
664                 n.deSerialize(&data[8], ser_version);
665                 
666                 addNode(p, n);
667         }
668         else if(command == TOCLIENT_BLOCKDATA)
669         {
670                 // Ignore too small packet
671                 if(datasize < 8)
672                         return;
673                         
674                 v3s16 p;
675                 p.X = readS16(&data[2]);
676                 p.Y = readS16(&data[4]);
677                 p.Z = readS16(&data[6]);
678                 
679                 /*dout_client<<DTIME<<"Client: Thread: BLOCKDATA for ("
680                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
681                 /*dstream<<DTIME<<"Client: Thread: BLOCKDATA for ("
682                                 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
683                 
684                 std::string datastring((char*)&data[8], datasize-8);
685                 std::istringstream istr(datastring, std::ios_base::binary);
686                 
687                 MapSector *sector;
688                 MapBlock *block;
689                 
690                 { //envlock
691                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
692                         
693                         v2s16 p2d(p.X, p.Z);
694                         sector = m_env.getMap().emergeSector(p2d);
695                         
696                         v2s16 sp = sector->getPos();
697                         if(sp != p2d)
698                         {
699                                 dstream<<"ERROR: Got sector with getPos()="
700                                                 <<"("<<sp.X<<","<<sp.Y<<"), tried to get"
701                                                 <<"("<<p2d.X<<","<<p2d.Y<<")"<<std::endl;
702                         }
703
704                         assert(sp == p2d);
705                         //assert(sector->getPos() == p2d);
706
707                         //TimeTaker timer("MapBlock deSerialize");
708                         // 0ms
709                         
710                         try{
711                                 block = sector->getBlockNoCreate(p.Y);
712                                 /*
713                                         Update an existing block
714                                 */
715                                 //dstream<<"Updating"<<std::endl;
716                                 block->deSerialize(istr, ser_version);
717                                 //block->setChangedFlag();
718                         }
719                         catch(InvalidPositionException &e)
720                         {
721                                 /*
722                                         Create a new block
723                                 */
724                                 //dstream<<"Creating new"<<std::endl;
725                                 block = new MapBlock(&m_env.getMap(), p);
726                                 block->deSerialize(istr, ser_version);
727                                 sector->insertBlock(block);
728                                 //block->setChangedFlag();
729
730                                 //DEBUG
731                                 /*NodeMod mod;
732                                 mod.type = NODEMOD_CHANGECONTENT;
733                                 mod.param = CONTENT_MESE;
734                                 block->setTempMod(v3s16(8,10,8), mod);
735                                 block->setTempMod(v3s16(8,9,8), mod);
736                                 block->setTempMod(v3s16(8,8,8), mod);
737                                 block->setTempMod(v3s16(8,7,8), mod);
738                                 block->setTempMod(v3s16(8,6,8), mod);*/
739 #if 0
740                                 /*
741                                         Add some coulds
742                                         Well, this is a dumb way to do it, they should just
743                                         be drawn as separate objects. But the looks of them
744                                         can be tested this way.
745                                 */
746                                 if(p.Y == 3)
747                                 {
748                                         NodeMod mod;
749                                         mod.type = NODEMOD_CHANGECONTENT;
750                                         mod.param = CONTENT_CLOUD;
751                                         v3s16 p2;
752                                         p2.Y = 8;
753                                         for(p2.X=3; p2.X<=13; p2.X++)
754                                         for(p2.Z=3; p2.Z<=13; p2.Z++)
755                                         {
756                                                 block->setTempMod(p2, mod);
757                                         }
758                                 }
759 #endif
760                         }
761                 } //envlock
762
763 #if 0
764                 /*
765                         Acknowledge block
766                 */
767                 /*
768                         [0] u16 command
769                         [2] u8 count
770                         [3] v3s16 pos_0
771                         [3+6] v3s16 pos_1
772                         ...
773                 */
774                 u32 replysize = 2+1+6;
775                 SharedBuffer<u8> reply(replysize);
776                 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
777                 reply[2] = 1;
778                 writeV3S16(&reply[3], p);
779                 // Send as reliable
780                 m_con.Send(PEER_ID_SERVER, 1, reply, true);
781 #endif
782
783                 /*
784                         Update Mesh of this block and blocks at x-, y- and z-.
785                         Environment should not be locked as it interlocks with the
786                         main thread, from which is will want to retrieve textures.
787                 */
788
789                 //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
790                 
791                 addUpdateMeshTaskWithEdge(p, true);
792         }
793         else if(command == TOCLIENT_PLAYERPOS)
794         {
795                 dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
796                                 <<std::endl;
797                 /*u16 our_peer_id;
798                 {
799                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
800                         our_peer_id = m_con.GetPeerID();
801                 }
802                 // Cancel if we don't have a peer id
803                 if(our_peer_id == PEER_ID_INEXISTENT){
804                         dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
805                                         "we have no peer id"
806                                         <<std::endl;
807                         return;
808                 }*/
809
810                 { //envlock
811                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
812                         
813                         u32 player_size = 2+12+12+4+4;
814                                 
815                         u32 player_count = (datasize-2) / player_size;
816                         u32 start = 2;
817                         for(u32 i=0; i<player_count; i++)
818                         {
819                                 u16 peer_id = readU16(&data[start]);
820
821                                 Player *player = m_env.getPlayer(peer_id);
822
823                                 // Skip if player doesn't exist
824                                 if(player == NULL)
825                                 {
826                                         start += player_size;
827                                         continue;
828                                 }
829
830                                 // Skip if player is local player
831                                 if(player->isLocal())
832                                 {
833                                         start += player_size;
834                                         continue;
835                                 }
836
837                                 v3s32 ps = readV3S32(&data[start+2]);
838                                 v3s32 ss = readV3S32(&data[start+2+12]);
839                                 s32 pitch_i = readS32(&data[start+2+12+12]);
840                                 s32 yaw_i = readS32(&data[start+2+12+12+4]);
841                                 /*dstream<<"Client: got "
842                                                 <<"pitch_i="<<pitch_i
843                                                 <<" yaw_i="<<yaw_i<<std::endl;*/
844                                 f32 pitch = (f32)pitch_i / 100.0;
845                                 f32 yaw = (f32)yaw_i / 100.0;
846                                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
847                                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
848                                 player->setPosition(position);
849                                 player->setSpeed(speed);
850                                 player->setPitch(pitch);
851                                 player->setYaw(yaw);
852
853                                 /*dstream<<"Client: player "<<peer_id
854                                                 <<" pitch="<<pitch
855                                                 <<" yaw="<<yaw<<std::endl;*/
856
857                                 start += player_size;
858                         }
859                 } //envlock
860         }
861         else if(command == TOCLIENT_PLAYERINFO)
862         {
863                 u16 our_peer_id;
864                 {
865                         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
866                         our_peer_id = m_con.GetPeerID();
867                 }
868                 // Cancel if we don't have a peer id
869                 if(our_peer_id == PEER_ID_INEXISTENT){
870                         dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
871                                         "we have no peer id"
872                                         <<std::endl;
873                         return;
874                 }
875                 
876                 //dstream<<DTIME<<"Client: Server reports players:"<<std::endl;
877
878                 { //envlock
879                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
880                         
881                         u32 item_size = 2+PLAYERNAME_SIZE;
882                         u32 player_count = (datasize-2) / item_size;
883                         u32 start = 2;
884                         // peer_ids
885                         core::list<u16> players_alive;
886                         for(u32 i=0; i<player_count; i++)
887                         {
888                                 // Make sure the name ends in '\0'
889                                 data[start+2+20-1] = 0;
890
891                                 u16 peer_id = readU16(&data[start]);
892
893                                 players_alive.push_back(peer_id);
894                                 
895                                 /*dstream<<DTIME<<"peer_id="<<peer_id
896                                                 <<" name="<<((char*)&data[start+2])<<std::endl;*/
897
898                                 // Don't update the info of the local player
899                                 if(peer_id == our_peer_id)
900                                 {
901                                         start += item_size;
902                                         continue;
903                                 }
904
905                                 Player *player = m_env.getPlayer(peer_id);
906
907                                 // Create a player if it doesn't exist
908                                 if(player == NULL)
909                                 {
910                                         player = new RemotePlayer(
911                                                         m_device->getSceneManager()->getRootSceneNode(),
912                                                         m_device,
913                                                         -1);
914                                         player->peer_id = peer_id;
915                                         m_env.addPlayer(player);
916                                         dout_client<<DTIME<<"Client: Adding new player "
917                                                         <<peer_id<<std::endl;
918                                 }
919                                 
920                                 player->updateName((char*)&data[start+2]);
921
922                                 start += item_size;
923                         }
924                         
925                         /*
926                                 Remove those players from the environment that
927                                 weren't listed by the server.
928                         */
929                         //dstream<<DTIME<<"Removing dead players"<<std::endl;
930                         core::list<Player*> players = m_env.getPlayers();
931                         core::list<Player*>::Iterator ip;
932                         for(ip=players.begin(); ip!=players.end(); ip++)
933                         {
934                                 // Ingore local player
935                                 if((*ip)->isLocal())
936                                         continue;
937                                 
938                                 // Warn about a special case
939                                 if((*ip)->peer_id == 0)
940                                 {
941                                         dstream<<DTIME<<"WARNING: Client: Removing "
942                                                         "dead player with id=0"<<std::endl;
943                                 }
944
945                                 bool is_alive = false;
946                                 core::list<u16>::Iterator i;
947                                 for(i=players_alive.begin(); i!=players_alive.end(); i++)
948                                 {
949                                         if((*ip)->peer_id == *i)
950                                         {
951                                                 is_alive = true;
952                                                 break;
953                                         }
954                                 }
955                                 /*dstream<<DTIME<<"peer_id="<<((*ip)->peer_id)
956                                                 <<" is_alive="<<is_alive<<std::endl;*/
957                                 if(is_alive)
958                                         continue;
959                                 dstream<<DTIME<<"Removing dead player "<<(*ip)->peer_id
960                                                 <<std::endl;
961                                 m_env.removePlayer((*ip)->peer_id);
962                         }
963                 } //envlock
964         }
965         else if(command == TOCLIENT_SECTORMETA)
966         {
967                 /*
968                         [0] u16 command
969                         [2] u8 sector count
970                         [3...] v2s16 pos + sector metadata
971                 */
972                 if(datasize < 3)
973                         return;
974
975                 //dstream<<"Client received TOCLIENT_SECTORMETA"<<std::endl;
976
977                 { //envlock
978                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
979                         
980                         std::string datastring((char*)&data[2], datasize-2);
981                         std::istringstream is(datastring, std::ios_base::binary);
982
983                         u8 buf[4];
984
985                         is.read((char*)buf, 1);
986                         u16 sector_count = readU8(buf);
987                         
988                         //dstream<<"sector_count="<<sector_count<<std::endl;
989
990                         for(u16 i=0; i<sector_count; i++)
991                         {
992                                 // Read position
993                                 is.read((char*)buf, 4);
994                                 v2s16 pos = readV2S16(buf);
995                                 /*dstream<<"Client: deserializing sector at "
996                                                 <<"("<<pos.X<<","<<pos.Y<<")"<<std::endl;*/
997                                 // Create sector
998                                 assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
999                                 ((ClientMap&)m_env.getMap()).deSerializeSector(pos, is);
1000                         }
1001                 } //envlock
1002         }
1003         else if(command == TOCLIENT_INVENTORY)
1004         {
1005                 if(datasize < 3)
1006                         return;
1007
1008                 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1009
1010                 { //envlock
1011                         //TimeTaker t2("mutex locking", m_device);
1012                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1013                         //t2.stop();
1014                         
1015                         //TimeTaker t3("istringstream init", m_device);
1016                         std::string datastring((char*)&data[2], datasize-2);
1017                         std::istringstream is(datastring, std::ios_base::binary);
1018                         //t3.stop();
1019                         
1020                         //m_env.printPlayers(dstream);
1021
1022                         //TimeTaker t4("player get", m_device);
1023                         Player *player = m_env.getLocalPlayer();
1024                         assert(player != NULL);
1025                         //t4.stop();
1026
1027                         //TimeTaker t1("inventory.deSerialize()", m_device);
1028                         player->inventory.deSerialize(is);
1029                         //t1.stop();
1030
1031                         m_inventory_updated = true;
1032
1033                         //dstream<<"Client got player inventory:"<<std::endl;
1034                         //player->inventory.print(dstream);
1035                 }
1036         }
1037         //DEBUG
1038         else if(command == TOCLIENT_OBJECTDATA)
1039         //else if(0)
1040         {
1041                 // Strip command word and create a stringstream
1042                 std::string datastring((char*)&data[2], datasize-2);
1043                 std::istringstream is(datastring, std::ios_base::binary);
1044                 
1045                 { //envlock
1046                 
1047                 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1048
1049                 u8 buf[12];
1050
1051                 /*
1052                         Read players
1053                 */
1054
1055                 is.read((char*)buf, 2);
1056                 u16 playercount = readU16(buf);
1057                 
1058                 for(u16 i=0; i<playercount; i++)
1059                 {
1060                         is.read((char*)buf, 2);
1061                         u16 peer_id = readU16(buf);
1062                         is.read((char*)buf, 12);
1063                         v3s32 p_i = readV3S32(buf);
1064                         is.read((char*)buf, 12);
1065                         v3s32 s_i = readV3S32(buf);
1066                         is.read((char*)buf, 4);
1067                         s32 pitch_i = readS32(buf);
1068                         is.read((char*)buf, 4);
1069                         s32 yaw_i = readS32(buf);
1070                         
1071                         Player *player = m_env.getPlayer(peer_id);
1072
1073                         // Skip if player doesn't exist
1074                         if(player == NULL)
1075                         {
1076                                 continue;
1077                         }
1078
1079                         // Skip if player is local player
1080                         if(player->isLocal())
1081                         {
1082                                 continue;
1083                         }
1084         
1085                         f32 pitch = (f32)pitch_i / 100.0;
1086                         f32 yaw = (f32)yaw_i / 100.0;
1087                         v3f position((f32)p_i.X/100., (f32)p_i.Y/100., (f32)p_i.Z/100.);
1088                         v3f speed((f32)s_i.X/100., (f32)s_i.Y/100., (f32)s_i.Z/100.);
1089                         
1090                         player->setPosition(position);
1091                         player->setSpeed(speed);
1092                         player->setPitch(pitch);
1093                         player->setYaw(yaw);
1094                 }
1095
1096                 /*
1097                         Read block objects
1098                 */
1099
1100                 // Read active block count
1101                 is.read((char*)buf, 2);
1102                 u16 blockcount = readU16(buf);
1103                 
1104                 // Initialize delete queue with all active blocks
1105                 core::map<v3s16, bool> abs_to_delete;
1106                 for(core::map<v3s16, bool>::Iterator
1107                                 i = m_active_blocks.getIterator();
1108                                 i.atEnd() == false; i++)
1109                 {
1110                         v3s16 p = i.getNode()->getKey();
1111                         /*dstream<<"adding "
1112                                         <<"("<<p.x<<","<<p.y<<","<<p.z<<") "
1113                                         <<" to abs_to_delete"
1114                                         <<std::endl;*/
1115                         abs_to_delete.insert(p, true);
1116                 }
1117
1118                 /*dstream<<"Initial delete queue size: "<<abs_to_delete.size()
1119                                 <<std::endl;*/
1120                 
1121                 for(u16 i=0; i<blockcount; i++)
1122                 {
1123                         // Read blockpos
1124                         is.read((char*)buf, 6);
1125                         v3s16 p = readV3S16(buf);
1126                         // Get block from somewhere
1127                         MapBlock *block = NULL;
1128                         try{
1129                                 block = m_env.getMap().getBlockNoCreate(p);
1130                         }
1131                         catch(InvalidPositionException &e)
1132                         {
1133                                 //TODO: Create a dummy block?
1134                         }
1135                         if(block == NULL)
1136                         {
1137                                 dstream<<"WARNING: "
1138                                                 <<"Could not get block at blockpos "
1139                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
1140                                                 <<"in TOCLIENT_OBJECTDATA. Ignoring "
1141                                                 <<"following block object data."
1142                                                 <<std::endl;
1143                                 return;
1144                         }
1145
1146                         /*dstream<<"Client updating objects for block "
1147                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1148                                         <<std::endl;*/
1149
1150                         // Insert to active block list
1151                         m_active_blocks.insert(p, true);
1152
1153                         // Remove from deletion queue
1154                         if(abs_to_delete.find(p) != NULL)
1155                                 abs_to_delete.remove(p);
1156
1157                         /*
1158                                 Update objects of block
1159                                 
1160                                 NOTE: Be sure this is done in the main thread.
1161                         */
1162                         block->updateObjects(is, m_server_ser_ver,
1163                                         m_device->getSceneManager(), m_env.getDayNightRatio());
1164                 }
1165                 
1166                 /*dstream<<"Final delete queue size: "<<abs_to_delete.size()
1167                                 <<std::endl;*/
1168                 
1169                 // Delete objects of blocks in delete queue
1170                 for(core::map<v3s16, bool>::Iterator
1171                                 i = abs_to_delete.getIterator();
1172                                 i.atEnd() == false; i++)
1173                 {
1174                         v3s16 p = i.getNode()->getKey();
1175                         try
1176                         {
1177                                 MapBlock *block = m_env.getMap().getBlockNoCreate(p);
1178                                 
1179                                 // Clear objects
1180                                 block->clearObjects();
1181                                 // Remove from active blocks list
1182                                 m_active_blocks.remove(p);
1183                         }
1184                         catch(InvalidPositionException &e)
1185                         {
1186                                 dstream<<"WARNAING: Client: "
1187                                                 <<"Couldn't clear objects of active->inactive"
1188                                                 <<" block "
1189                                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1190                                                 <<" because block was not found"
1191                                                 <<std::endl;
1192                                 // Ignore
1193                         }
1194                 }
1195
1196                 } //envlock
1197         }
1198         else if(command == TOCLIENT_TIME_OF_DAY)
1199         {
1200                 if(datasize < 4)
1201                         return;
1202                 
1203                 u16 time_of_day = readU16(&data[2]);
1204                 time_of_day = time_of_day % 24000;
1205                 //dstream<<"Client: time_of_day="<<time_of_day<<std::endl;
1206                 
1207                 /*
1208                         time_of_day:
1209                         0 = midnight
1210                         12000 = midday
1211                 */
1212                 {
1213                         m_env.setTimeOfDay(time_of_day);
1214
1215                         u32 dr = m_env.getDayNightRatio();
1216
1217                         dstream<<"Client: time_of_day="<<time_of_day
1218                                         <<", dr="<<dr
1219                                         <<std::endl;
1220                 }
1221
1222         }
1223         else if(command == TOCLIENT_CHAT_MESSAGE)
1224         {
1225                 /*
1226                         u16 command
1227                         u16 length
1228                         wstring message
1229                 */
1230                 u8 buf[6];
1231                 std::string datastring((char*)&data[2], datasize-2);
1232                 std::istringstream is(datastring, std::ios_base::binary);
1233                 
1234                 // Read stuff
1235                 is.read((char*)buf, 2);
1236                 u16 len = readU16(buf);
1237                 
1238                 std::wstring message;
1239                 for(u16 i=0; i<len; i++)
1240                 {
1241                         is.read((char*)buf, 2);
1242                         message += (wchar_t)readU16(buf);
1243                 }
1244
1245                 /*dstream<<"Client received chat message: "
1246                                 <<wide_to_narrow(message)<<std::endl;*/
1247                 
1248                 m_chat_queue.push_back(message);
1249         }
1250         else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1251         {
1252                 //if(g_settings.getBool("enable_experimental"))
1253                 {
1254                         /*
1255                                 u16 command
1256                                 u16 count of removed objects
1257                                 for all removed objects {
1258                                         u16 id
1259                                 }
1260                                 u16 count of added objects
1261                                 for all added objects {
1262                                         u16 id
1263                                         u8 type
1264                                         u16 initialization data length
1265                                         string initialization data
1266                                 }
1267                         */
1268
1269                         char buf[6];
1270                         // Get all data except the command number
1271                         std::string datastring((char*)&data[2], datasize-2);
1272                         // Throw them in an istringstream
1273                         std::istringstream is(datastring, std::ios_base::binary);
1274
1275                         // Read stuff
1276                         
1277                         // Read removed objects
1278                         is.read(buf, 2);
1279                         u16 removed_count = readU16((u8*)buf);
1280                         for(u16 i=0; i<removed_count; i++)
1281                         {
1282                                 is.read(buf, 2);
1283                                 u16 id = readU16((u8*)buf);
1284                                 // Remove it
1285                                 {
1286                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1287                                         m_env.removeActiveObject(id);
1288                                 }
1289                         }
1290                         
1291                         // Read added objects
1292                         is.read(buf, 2);
1293                         u16 added_count = readU16((u8*)buf);
1294                         for(u16 i=0; i<added_count; i++)
1295                         {
1296                                 is.read(buf, 2);
1297                                 u16 id = readU16((u8*)buf);
1298                                 is.read(buf, 1);
1299                                 u8 type = readU8((u8*)buf);
1300                                 std::string data = deSerializeLongString(is);
1301                                 // Add it
1302                                 {
1303                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1304                                         m_env.addActiveObject(id, type, data);
1305                                 }
1306                         }
1307                 }
1308         }
1309         else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1310         {
1311                 //if(g_settings.getBool("enable_experimental"))
1312                 {
1313                         /*
1314                                 u16 command
1315                                 for all objects
1316                                 {
1317                                         u16 id
1318                                         u16 message length
1319                                         string message
1320                                 }
1321                         */
1322                         char buf[6];
1323                         // Get all data except the command number
1324                         std::string datastring((char*)&data[2], datasize-2);
1325                         // Throw them in an istringstream
1326                         std::istringstream is(datastring, std::ios_base::binary);
1327                         
1328                         while(is.eof() == false)
1329                         {
1330                                 // Read stuff
1331                                 is.read(buf, 2);
1332                                 u16 id = readU16((u8*)buf);
1333                                 if(is.eof())
1334                                         break;
1335                                 is.read(buf, 2);
1336                                 u16 message_size = readU16((u8*)buf);
1337                                 std::string message;
1338                                 message.reserve(message_size);
1339                                 for(u16 i=0; i<message_size; i++)
1340                                 {
1341                                         is.read(buf, 1);
1342                                         message.append(buf, 1);
1343                                 }
1344                                 // Pass on to the environment
1345                                 {
1346                                         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1347                                         m_env.processActiveObjectMessage(id, message);
1348                                 }
1349                         }
1350                 }
1351         }
1352         else if(command == TOCLIENT_HP)
1353         {
1354                 std::string datastring((char*)&data[2], datasize-2);
1355                 std::istringstream is(datastring, std::ios_base::binary);
1356                 Player *player = m_env.getLocalPlayer();
1357                 assert(player != NULL);
1358                 u8 hp = readU8(is);
1359                 player->hp = hp;
1360         }
1361         else if(command == TOCLIENT_MOVE_PLAYER)
1362         {
1363                 std::string datastring((char*)&data[2], datasize-2);
1364                 std::istringstream is(datastring, std::ios_base::binary);
1365                 Player *player = m_env.getLocalPlayer();
1366                 assert(player != NULL);
1367                 v3f pos = readV3F1000(is);
1368                 f32 pitch = readF1000(is);
1369                 f32 yaw = readF1000(is);
1370                 player->setPosition(pos);
1371                 /*player->setPitch(pitch);
1372                 player->setYaw(yaw);*/
1373
1374                 dstream<<"Client got TOCLIENT_MOVE_PLAYER"
1375                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1376                                 <<" pitch="<<pitch
1377                                 <<" yaw="<<yaw
1378                                 <<std::endl;
1379
1380                 /*
1381                         Add to ClientEvent queue.
1382                         This has to be sent to the main program because otherwise
1383                         it would just force the pitch and yaw values to whatever
1384                         the camera points to.
1385                 */
1386                 ClientEvent event;
1387                 event.type = CE_PLAYER_FORCE_MOVE;
1388                 event.player_force_move.pitch = pitch;
1389                 event.player_force_move.yaw = yaw;
1390                 m_client_event_queue.push_back(event);
1391
1392                 // Ignore damage for a few seconds, so that the player doesn't
1393                 // get damage from falling on ground
1394                 m_ignore_damage_timer = 3.0;
1395         }
1396         else
1397         {
1398                 dout_client<<DTIME<<"WARNING: Client: Ignoring unknown command "
1399                                 <<command<<std::endl;
1400         }
1401 }
1402
1403 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1404 {
1405         //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1406         m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1407 }
1408
1409 void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
1410                 v3s16 nodepos_oversurface, u16 item)
1411 {
1412         if(connectedAndInitialized() == false){
1413                 dout_client<<DTIME<<"Client::groundAction() "
1414                                 "cancelled (not connected)"
1415                                 <<std::endl;
1416                 return;
1417         }
1418         
1419         /*
1420                 length: 17
1421                 [0] u16 command
1422                 [2] u8 action
1423                 [3] v3s16 nodepos_undersurface
1424                 [9] v3s16 nodepos_abovesurface
1425                 [15] u16 item
1426                 actions:
1427                 0: start digging
1428                 1: place block
1429                 2: stop digging (all parameters ignored)
1430                 3: digging completed
1431         */
1432         u8 datasize = 2 + 1 + 6 + 6 + 2;
1433         SharedBuffer<u8> data(datasize);
1434         writeU16(&data[0], TOSERVER_GROUND_ACTION);
1435         writeU8(&data[2], action);
1436         writeV3S16(&data[3], nodepos_undersurface);
1437         writeV3S16(&data[9], nodepos_oversurface);
1438         writeU16(&data[15], item);
1439         Send(0, data, true);
1440 }
1441
1442 void Client::clickObject(u8 button, v3s16 blockpos, s16 id, u16 item)
1443 {
1444         if(connectedAndInitialized() == false){
1445                 dout_client<<DTIME<<"Client::clickObject() "
1446                                 "cancelled (not connected)"
1447                                 <<std::endl;
1448                 return;
1449         }
1450         
1451         /*
1452                 [0] u16 command=TOSERVER_CLICK_OBJECT
1453                 [2] u8 button (0=left, 1=right)
1454                 [3] v3s16 block
1455                 [9] s16 id
1456                 [11] u16 item
1457         */
1458         u8 datasize = 2 + 1 + 6 + 2 + 2;
1459         SharedBuffer<u8> data(datasize);
1460         writeU16(&data[0], TOSERVER_CLICK_OBJECT);
1461         writeU8(&data[2], button);
1462         writeV3S16(&data[3], blockpos);
1463         writeS16(&data[9], id);
1464         writeU16(&data[11], item);
1465         Send(0, data, true);
1466 }
1467
1468 void Client::clickActiveObject(u8 button, u16 id, u16 item)
1469 {
1470         if(connectedAndInitialized() == false){
1471                 dout_client<<DTIME<<"Client::clickActiveObject() "
1472                                 "cancelled (not connected)"
1473                                 <<std::endl;
1474                 return;
1475         }
1476         
1477         /*
1478                 length: 7
1479                 [0] u16 command
1480                 [2] u8 button (0=left, 1=right)
1481                 [3] u16 id
1482                 [5] u16 item
1483         */
1484         u8 datasize = 2 + 1 + 6 + 2 + 2;
1485         SharedBuffer<u8> data(datasize);
1486         writeU16(&data[0], TOSERVER_CLICK_ACTIVEOBJECT);
1487         writeU8(&data[2], button);
1488         writeU16(&data[3], id);
1489         writeU16(&data[5], item);
1490         Send(0, data, true);
1491 }
1492
1493 void Client::sendSignText(v3s16 blockpos, s16 id, std::string text)
1494 {
1495         /*
1496                 u16 command
1497                 v3s16 blockpos
1498                 s16 id
1499                 u16 textlen
1500                 textdata
1501         */
1502         std::ostringstream os(std::ios_base::binary);
1503         u8 buf[12];
1504         
1505         // Write command
1506         writeU16(buf, TOSERVER_SIGNTEXT);
1507         os.write((char*)buf, 2);
1508         
1509         // Write blockpos
1510         writeV3S16(buf, blockpos);
1511         os.write((char*)buf, 6);
1512
1513         // Write id
1514         writeS16(buf, id);
1515         os.write((char*)buf, 2);
1516
1517         u16 textlen = text.size();
1518         // Write text length
1519         writeS16(buf, textlen);
1520         os.write((char*)buf, 2);
1521
1522         // Write text
1523         os.write((char*)text.c_str(), textlen);
1524         
1525         // Make data buffer
1526         std::string s = os.str();
1527         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1528         // Send as reliable
1529         Send(0, data, true);
1530 }
1531         
1532 void Client::sendSignNodeText(v3s16 p, std::string text)
1533 {
1534         /*
1535                 u16 command
1536                 v3s16 p
1537                 u16 textlen
1538                 textdata
1539         */
1540         std::ostringstream os(std::ios_base::binary);
1541         u8 buf[12];
1542         
1543         // Write command
1544         writeU16(buf, TOSERVER_SIGNNODETEXT);
1545         os.write((char*)buf, 2);
1546         
1547         // Write p
1548         writeV3S16(buf, p);
1549         os.write((char*)buf, 6);
1550
1551         u16 textlen = text.size();
1552         // Write text length
1553         writeS16(buf, textlen);
1554         os.write((char*)buf, 2);
1555
1556         // Write text
1557         os.write((char*)text.c_str(), textlen);
1558         
1559         // Make data buffer
1560         std::string s = os.str();
1561         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1562         // Send as reliable
1563         Send(0, data, true);
1564 }
1565         
1566 void Client::sendInventoryAction(InventoryAction *a)
1567 {
1568         std::ostringstream os(std::ios_base::binary);
1569         u8 buf[12];
1570         
1571         // Write command
1572         writeU16(buf, TOSERVER_INVENTORY_ACTION);
1573         os.write((char*)buf, 2);
1574
1575         a->serialize(os);
1576         
1577         // Make data buffer
1578         std::string s = os.str();
1579         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1580         // Send as reliable
1581         Send(0, data, true);
1582 }
1583
1584 void Client::sendChatMessage(const std::wstring &message)
1585 {
1586         std::ostringstream os(std::ios_base::binary);
1587         u8 buf[12];
1588         
1589         // Write command
1590         writeU16(buf, TOSERVER_CHAT_MESSAGE);
1591         os.write((char*)buf, 2);
1592         
1593         // Write length
1594         writeU16(buf, message.size());
1595         os.write((char*)buf, 2);
1596         
1597         // Write string
1598         for(u32 i=0; i<message.size(); i++)
1599         {
1600                 u16 w = message[i];
1601                 writeU16(buf, w);
1602                 os.write((char*)buf, 2);
1603         }
1604         
1605         // Make data buffer
1606         std::string s = os.str();
1607         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1608         // Send as reliable
1609         Send(0, data, true);
1610 }
1611
1612 void Client::sendChangePassword(const std::wstring oldpassword,
1613                 const std::wstring newpassword)
1614 {
1615         Player *player = m_env.getLocalPlayer();
1616         if(player == NULL)
1617                 return;
1618
1619         std::string playername = player->getName();
1620         std::string oldpwd = translatePassword(playername, oldpassword);
1621         std::string newpwd = translatePassword(playername, newpassword);
1622
1623         std::ostringstream os(std::ios_base::binary);
1624         u8 buf[2+PASSWORD_SIZE*2];
1625         /*
1626                 [0] u16 TOSERVER_PASSWORD
1627                 [2] u8[28] old password
1628                 [30] u8[28] new password
1629         */
1630
1631         writeU16(buf, TOSERVER_PASSWORD);
1632         for(u32 i=0;i<PASSWORD_SIZE-1;i++)
1633         {
1634                 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
1635                 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
1636         }
1637         buf[2+PASSWORD_SIZE-1] = 0;
1638         buf[30+PASSWORD_SIZE-1] = 0;
1639         os.write((char*)buf, 2+PASSWORD_SIZE*2);
1640
1641         // Make data buffer
1642         std::string s = os.str();
1643         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1644         // Send as reliable
1645         Send(0, data, true);
1646 }
1647
1648
1649 void Client::sendDamage(u8 damage)
1650 {
1651         DSTACK(__FUNCTION_NAME);
1652         std::ostringstream os(std::ios_base::binary);
1653
1654         writeU16(os, TOSERVER_DAMAGE);
1655         writeU8(os, damage);
1656
1657         // Make data buffer
1658         std::string s = os.str();
1659         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1660         // Send as reliable
1661         Send(0, data, true);
1662 }
1663
1664 void Client::sendPlayerPos()
1665 {
1666         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1667         
1668         Player *myplayer = m_env.getLocalPlayer();
1669         if(myplayer == NULL)
1670                 return;
1671         
1672         u16 our_peer_id;
1673         {
1674                 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1675                 our_peer_id = m_con.GetPeerID();
1676         }
1677         
1678         // Set peer id if not set already
1679         if(myplayer->peer_id == PEER_ID_INEXISTENT)
1680                 myplayer->peer_id = our_peer_id;
1681         // Check that an existing peer_id is the same as the connection's
1682         assert(myplayer->peer_id == our_peer_id);
1683         
1684         v3f pf = myplayer->getPosition();
1685         v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
1686         v3f sf = myplayer->getSpeed();
1687         v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
1688         s32 pitch = myplayer->getPitch() * 100;
1689         s32 yaw = myplayer->getYaw() * 100;
1690
1691         /*
1692                 Format:
1693                 [0] u16 command
1694                 [2] v3s32 position*100
1695                 [2+12] v3s32 speed*100
1696                 [2+12+12] s32 pitch*100
1697                 [2+12+12+4] s32 yaw*100
1698         */
1699
1700         SharedBuffer<u8> data(2+12+12+4+4);
1701         writeU16(&data[0], TOSERVER_PLAYERPOS);
1702         writeV3S32(&data[2], position);
1703         writeV3S32(&data[2+12], speed);
1704         writeS32(&data[2+12+12], pitch);
1705         writeS32(&data[2+12+12+4], yaw);
1706
1707         // Send as unreliable
1708         Send(0, data, false);
1709 }
1710
1711 void Client::removeNode(v3s16 p)
1712 {
1713         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1714         
1715         core::map<v3s16, MapBlock*> modified_blocks;
1716
1717         try
1718         {
1719                 //TimeTaker t("removeNodeAndUpdate", m_device);
1720                 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
1721         }
1722         catch(InvalidPositionException &e)
1723         {
1724         }
1725         
1726         for(core::map<v3s16, MapBlock * >::Iterator
1727                         i = modified_blocks.getIterator();
1728                         i.atEnd() == false; i++)
1729         {
1730                 v3s16 p = i.getNode()->getKey();
1731                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1732                 addUpdateMeshTaskWithEdge(p);
1733         }
1734 }
1735
1736 void Client::addNode(v3s16 p, MapNode n)
1737 {
1738         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1739
1740         TimeTaker timer1("Client::addNode()");
1741
1742         core::map<v3s16, MapBlock*> modified_blocks;
1743
1744         try
1745         {
1746                 TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
1747                 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
1748         }
1749         catch(InvalidPositionException &e)
1750         {}
1751         
1752         //TimeTaker timer2("Client::addNode(): updateMeshes");
1753
1754         for(core::map<v3s16, MapBlock * >::Iterator
1755                         i = modified_blocks.getIterator();
1756                         i.atEnd() == false; i++)
1757         {
1758                 v3s16 p = i.getNode()->getKey();
1759                 //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
1760                 addUpdateMeshTaskWithEdge(p);
1761         }
1762 }
1763         
1764 void Client::updateCamera(v3f pos, v3f dir)
1765 {
1766         m_env.getClientMap().updateCamera(pos, dir);
1767         camera_position = pos;
1768         camera_direction = dir;
1769 }
1770
1771 MapNode Client::getNode(v3s16 p)
1772 {
1773         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1774         return m_env.getMap().getNode(p);
1775 }
1776
1777 NodeMetadata* Client::getNodeMetadata(v3s16 p)
1778 {
1779         return m_env.getMap().getNodeMetadata(p);
1780 }
1781
1782 v3f Client::getPlayerPosition()
1783 {
1784         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1785         LocalPlayer *player = m_env.getLocalPlayer();
1786         assert(player != NULL);
1787         return player->getPosition();
1788 }
1789
1790 void Client::setPlayerControl(PlayerControl &control)
1791 {
1792         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1793         LocalPlayer *player = m_env.getLocalPlayer();
1794         assert(player != NULL);
1795         player->control = control;
1796 }
1797
1798 // Returns true if the inventory of the local player has been
1799 // updated from the server. If it is true, it is set to false.
1800 bool Client::getLocalInventoryUpdated()
1801 {
1802         // m_inventory_updated is behind envlock
1803         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1804         bool updated = m_inventory_updated;
1805         m_inventory_updated = false;
1806         return updated;
1807 }
1808
1809 // Copies the inventory of the local player to parameter
1810 void Client::getLocalInventory(Inventory &dst)
1811 {
1812         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1813         Player *player = m_env.getLocalPlayer();
1814         assert(player != NULL);
1815         dst = player->inventory;
1816 }
1817
1818 InventoryContext *Client::getInventoryContext()
1819 {
1820         return &m_inventory_context;
1821 }
1822
1823 Inventory* Client::getInventory(InventoryContext *c, std::string id)
1824 {
1825         if(id == "current_player")
1826         {
1827                 assert(c->current_player);
1828                 return &(c->current_player->inventory);
1829         }
1830         
1831         Strfnd fn(id);
1832         std::string id0 = fn.next(":");
1833
1834         if(id0 == "nodemeta")
1835         {
1836                 v3s16 p;
1837                 p.X = stoi(fn.next(","));
1838                 p.Y = stoi(fn.next(","));
1839                 p.Z = stoi(fn.next(","));
1840                 NodeMetadata* meta = getNodeMetadata(p);
1841                 if(meta)
1842                         return meta->getInventory();
1843                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
1844                                 <<"no metadata found"<<std::endl;
1845                 return NULL;
1846         }
1847
1848         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
1849         return NULL;
1850 }
1851 void Client::inventoryAction(InventoryAction *a)
1852 {
1853         sendInventoryAction(a);
1854 }
1855
1856 MapBlockObject * Client::getSelectedObject(
1857                 f32 max_d,
1858                 v3f from_pos_f_on_map,
1859                 core::line3d<f32> shootline_on_map
1860         )
1861 {
1862         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1863
1864         core::array<DistanceSortedObject> objects;
1865
1866         for(core::map<v3s16, bool>::Iterator
1867                         i = m_active_blocks.getIterator();
1868                         i.atEnd() == false; i++)
1869         {
1870                 v3s16 p = i.getNode()->getKey();
1871
1872                 MapBlock *block = NULL;
1873                 try
1874                 {
1875                         block = m_env.getMap().getBlockNoCreate(p);
1876                 }
1877                 catch(InvalidPositionException &e)
1878                 {
1879                         continue;
1880                 }
1881
1882                 // Calculate from_pos relative to block
1883                 v3s16 block_pos_i_on_map = block->getPosRelative();
1884                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1885                 v3f from_pos_f_on_block = from_pos_f_on_map - block_pos_f_on_map;
1886
1887                 block->getObjects(from_pos_f_on_block, max_d, objects);
1888                 //block->getPseudoObjects(from_pos_f_on_block, max_d, objects);
1889         }
1890
1891         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1892         
1893         // Sort them.
1894         // After this, the closest object is the first in the array.
1895         objects.sort();
1896
1897         for(u32 i=0; i<objects.size(); i++)
1898         {
1899                 MapBlockObject *obj = objects[i].obj;
1900                 MapBlock *block = obj->getBlock();
1901
1902                 // Calculate shootline relative to block
1903                 v3s16 block_pos_i_on_map = block->getPosRelative();
1904                 v3f block_pos_f_on_map = intToFloat(block_pos_i_on_map, BS);
1905                 core::line3d<f32> shootline_on_block(
1906                                 shootline_on_map.start - block_pos_f_on_map,
1907                                 shootline_on_map.end - block_pos_f_on_map
1908                 );
1909
1910                 if(obj->isSelected(shootline_on_block))
1911                 {
1912                         //dstream<<"Returning selected object"<<std::endl;
1913                         return obj;
1914                 }
1915         }
1916
1917         //dstream<<"No object selected; returning NULL."<<std::endl;
1918         return NULL;
1919 }
1920
1921 ClientActiveObject * Client::getSelectedActiveObject(
1922                 f32 max_d,
1923                 v3f from_pos_f_on_map,
1924                 core::line3d<f32> shootline_on_map
1925         )
1926 {
1927         core::array<DistanceSortedActiveObject> objects;
1928
1929         m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
1930
1931         //dstream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
1932         
1933         // Sort them.
1934         // After this, the closest object is the first in the array.
1935         objects.sort();
1936
1937         for(u32 i=0; i<objects.size(); i++)
1938         {
1939                 ClientActiveObject *obj = objects[i].obj;
1940                 
1941                 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
1942                 if(selection_box == NULL)
1943                         continue;
1944
1945                 v3f pos = obj->getPosition();
1946
1947                 core::aabbox3d<f32> offsetted_box(
1948                                 selection_box->MinEdge + pos,
1949                                 selection_box->MaxEdge + pos
1950                 );
1951
1952                 if(offsetted_box.intersectsWithLine(shootline_on_map))
1953                 {
1954                         //dstream<<"Returning selected object"<<std::endl;
1955                         return obj;
1956                 }
1957         }
1958
1959         //dstream<<"No object selected; returning NULL."<<std::endl;
1960         return NULL;
1961 }
1962
1963 void Client::printDebugInfo(std::ostream &os)
1964 {
1965         //JMutexAutoLock lock1(m_fetchblock_mutex);
1966         /*JMutexAutoLock lock2(m_incoming_queue_mutex);
1967
1968         os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
1969                 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
1970                 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
1971                 <<std::endl;*/
1972 }
1973         
1974 /*s32 Client::getDayNightIndex()
1975 {
1976         assert(m_daynight_i >= 0 && m_daynight_i < DAYNIGHT_CACHE_COUNT);
1977         return m_daynight_i;
1978 }*/
1979
1980 u32 Client::getDayNightRatio()
1981 {
1982         //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1983         return m_env.getDayNightRatio();
1984 }
1985
1986 u16 Client::getHP()
1987 {
1988         Player *player = m_env.getLocalPlayer();
1989         assert(player != NULL);
1990         return player->hp;
1991 }
1992
1993 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
1994 {
1995         /*dstream<<"Client::addUpdateMeshTask(): "
1996                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1997                         <<std::endl;*/
1998
1999         MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2000         if(b == NULL)
2001                 return;
2002
2003         /*
2004                 Create a task to update the mesh of the block
2005         */
2006         
2007         MeshMakeData *data = new MeshMakeData;
2008         
2009         {
2010                 //TimeTaker timer("data fill");
2011                 // 0ms
2012                 data->fill(getDayNightRatio(), b);
2013         }
2014
2015         // Debug wait
2016         //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2017         
2018         // Add task to queue
2019         m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
2020
2021         /*dstream<<"Mesh update input queue size is "
2022                         <<m_mesh_update_thread.m_queue_in.size()
2023                         <<std::endl;*/
2024         
2025 #if 0
2026         // Temporary test: make mesh directly in here
2027         {
2028                 //TimeTaker timer("make mesh");
2029                 // 10ms
2030                 scene::SMesh *mesh_new = NULL;
2031                 mesh_new = makeMapBlockMesh(data);
2032                 b->replaceMesh(mesh_new);
2033                 delete data;
2034         }
2035 #endif
2036
2037         b->setMeshExpired(false);
2038 }
2039
2040 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
2041 {
2042         /*{
2043                 v3s16 p = blockpos;
2044                 dstream<<"Client::addUpdateMeshTaskWithEdge(): "
2045                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2046                                 <<std::endl;
2047         }*/
2048
2049         try{
2050                 v3s16 p = blockpos + v3s16(0,0,0);
2051                 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2052                 addUpdateMeshTask(p, ack_to_server);
2053         }
2054         catch(InvalidPositionException &e){}
2055         // Leading edge
2056         try{
2057                 v3s16 p = blockpos + v3s16(-1,0,0);
2058                 addUpdateMeshTask(p);
2059         }
2060         catch(InvalidPositionException &e){}
2061         try{
2062                 v3s16 p = blockpos + v3s16(0,-1,0);
2063                 addUpdateMeshTask(p);
2064         }
2065         catch(InvalidPositionException &e){}
2066         try{
2067                 v3s16 p = blockpos + v3s16(0,0,-1);
2068                 addUpdateMeshTask(p);
2069         }
2070         catch(InvalidPositionException &e){}
2071 }
2072
2073 ClientEvent Client::getClientEvent()
2074 {
2075         if(m_client_event_queue.size() == 0)
2076         {
2077                 ClientEvent event;
2078                 event.type = CE_NONE;
2079                 return event;
2080         }
2081         return m_client_event_queue.pop_front();
2082 }
2083
2084