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