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