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