3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
23 #include "clientserver.h"
24 #include "jthread/jmutexautolock.h"
29 #include "mapsector.h"
30 #include "mapblock_mesh.h"
36 #include "nodemetadata.h"
40 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "clientmedia.h"
45 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "serialization.h"
48 #include "util/serialize.h"
50 #include "util/directiontables.h"
51 #include "util/pointedthing.h"
55 #include <curl/curl.h>
58 static std::string getMediaCacheDir()
60 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
67 QueuedMeshUpdate::QueuedMeshUpdate():
70 ack_block_to_server(false)
74 QueuedMeshUpdate::~QueuedMeshUpdate()
84 MeshUpdateQueue::MeshUpdateQueue()
88 MeshUpdateQueue::~MeshUpdateQueue()
90 JMutexAutoLock lock(m_mutex);
92 for(std::vector<QueuedMeshUpdate*>::iterator
94 i != m_queue.end(); i++)
96 QueuedMeshUpdate *q = *i;
102 peer_id=0 adds with nobody to send to
104 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
106 DSTACK(__FUNCTION_NAME);
110 JMutexAutoLock lock(m_mutex);
116 Find if block is already in queue.
117 If it is, update the data and quit.
119 for(std::vector<QueuedMeshUpdate*>::iterator
121 i != m_queue.end(); i++)
123 QueuedMeshUpdate *q = *i;
129 if(ack_block_to_server)
130 q->ack_block_to_server = true;
138 QueuedMeshUpdate *q = new QueuedMeshUpdate;
141 q->ack_block_to_server = ack_block_to_server;
142 m_queue.push_back(q);
145 // Returned pointer must be deleted
146 // Returns NULL if queue is empty
147 QueuedMeshUpdate * MeshUpdateQueue::pop()
149 JMutexAutoLock lock(m_mutex);
151 bool must_be_urgent = !m_urgents.empty();
152 for(std::vector<QueuedMeshUpdate*>::iterator
154 i != m_queue.end(); i++)
156 QueuedMeshUpdate *q = *i;
157 if(must_be_urgent && m_urgents.count(q->p) == 0)
160 m_urgents.erase(q->p);
170 void * MeshUpdateThread::Thread()
174 log_register_thread("MeshUpdateThread");
176 DSTACK(__FUNCTION_NAME);
178 BEGIN_DEBUG_EXCEPTION_HANDLER
180 while(!StopRequested())
182 /*// Wait for output queue to flush.
183 // Allow 2 in queue, this makes less frametime jitter.
184 // Umm actually, there is no much difference
185 if(m_queue_out.size() >= 2)
191 QueuedMeshUpdate *q = m_queue_in.pop();
198 ScopeProfiler sp(g_profiler, "Client: Mesh making");
200 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
201 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
210 r.ack_block_to_server = q->ack_block_to_server;
212 /*infostream<<"MeshUpdateThread: Processed "
213 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
216 m_queue_out.push_back(r);
221 END_DEBUG_EXCEPTION_HANDLER(errorstream)
231 IrrlichtDevice *device,
232 const char *playername,
233 std::string password,
234 MapDrawControl &control,
235 IWritableTextureSource *tsrc,
236 IWritableShaderSource *shsrc,
237 IWritableItemDefManager *itemdef,
238 IWritableNodeDefManager *nodedef,
239 ISoundManager *sound,
240 MtEventManager *event,
249 m_mesh_update_thread(this),
251 new ClientMap(this, this, control,
252 device->getSceneManager()->getRootSceneNode(),
253 device->getSceneManager(), 666),
254 device->getSceneManager(),
257 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
259 m_server_ser_ver(SER_FMT_VER_INVALID),
261 m_inventory_updated(false),
262 m_inventory_from_server(NULL),
263 m_inventory_from_server_age(0.0),
268 m_password(password),
269 m_access_denied(false),
270 m_itemdef_received(false),
271 m_nodedef_received(false),
272 m_media_downloader(new ClientMediaDownloader()),
273 m_time_of_day_set(false),
274 m_last_time_of_day_f(-1),
275 m_time_of_day_update_timer(0),
276 m_recommended_send_interval(0.1),
277 m_removed_sounds_check_timer(0)
279 m_packetcounter_timer = 0.0;
280 //m_delete_unused_sectors_timer = 0.0;
281 m_connection_reinit_timer = 0.0;
282 m_avg_rtt_timer = 0.0;
283 m_playerpos_send_timer = 0.0;
284 m_ignore_damage_timer = 0.0;
290 Player *player = new LocalPlayer(this);
292 player->updateName(playername);
294 m_env.addPlayer(player);
301 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
305 m_mesh_update_thread.Stop();
306 m_mesh_update_thread.Wait();
307 while(!m_mesh_update_thread.m_queue_out.empty()) {
308 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
313 delete m_inventory_from_server;
315 // Delete detached inventories
317 for(std::map<std::string, Inventory*>::iterator
318 i = m_detached_inventories.begin();
319 i != m_detached_inventories.end(); i++){
324 // cleanup 3d model meshes on client shutdown
325 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
326 scene::IAnimatedMesh * mesh =
327 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
330 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
334 void Client::connect(Address address)
336 DSTACK(__FUNCTION_NAME);
337 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
338 m_con.SetTimeoutMs(0);
339 m_con.Connect(address);
342 bool Client::connectedAndInitialized()
344 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
346 if(m_con.Connected() == false)
349 if(m_server_ser_ver == SER_FMT_VER_INVALID)
355 void Client::step(float dtime)
357 DSTACK(__FUNCTION_NAME);
363 if(m_ignore_damage_timer > dtime)
364 m_ignore_damage_timer -= dtime;
366 m_ignore_damage_timer = 0.0;
368 m_animation_time += dtime;
369 if(m_animation_time > 60.0)
370 m_animation_time -= 60.0;
372 m_time_of_day_update_timer += dtime;
374 //infostream<<"Client steps "<<dtime<<std::endl;
377 //TimeTaker timer("ReceiveAll()", m_device);
383 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
385 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
386 m_con.RunTimeouts(dtime);
393 float &counter = m_packetcounter_timer;
399 infostream<<"Client packetcounter (20s):"<<std::endl;
400 m_packetcounter.print(infostream);
401 m_packetcounter.clear();
405 // Get connection status
406 bool connected = connectedAndInitialized();
411 Delete unused sectors
413 NOTE: This jams the game for a while because deleting sectors
417 float &counter = m_delete_unused_sectors_timer;
425 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
427 core::list<v3s16> deleted_blocks;
429 float delete_unused_sectors_timeout =
430 g_settings->getFloat("client_delete_unused_sectors_timeout");
432 // Delete sector blocks
433 /*u32 num = m_env.getMap().unloadUnusedData
434 (delete_unused_sectors_timeout,
435 true, &deleted_blocks);*/
437 // Delete whole sectors
438 m_env.getMap().unloadUnusedData
439 (delete_unused_sectors_timeout,
442 if(deleted_blocks.size() > 0)
444 /*infostream<<"Client: Deleted blocks of "<<num
445 <<" unused sectors"<<std::endl;*/
446 /*infostream<<"Client: Deleted "<<num
447 <<" unused sectors"<<std::endl;*/
453 // Env is locked so con can be locked.
454 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
456 core::list<v3s16>::Iterator i = deleted_blocks.begin();
457 core::list<v3s16> sendlist;
460 if(sendlist.size() == 255 || i == deleted_blocks.end())
462 if(sendlist.size() == 0)
471 u32 replysize = 2+1+6*sendlist.size();
472 SharedBuffer<u8> reply(replysize);
473 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
474 reply[2] = sendlist.size();
476 for(core::list<v3s16>::Iterator
477 j = sendlist.begin();
478 j != sendlist.end(); j++)
480 writeV3S16(&reply[2+1+6*k], *j);
483 m_con.Send(PEER_ID_SERVER, 1, reply, true);
485 if(i == deleted_blocks.end())
491 sendlist.push_back(*i);
499 if(connected == false)
501 float &counter = m_connection_reinit_timer;
507 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
509 Player *myplayer = m_env.getLocalPlayer();
510 assert(myplayer != NULL);
512 // Send TOSERVER_INIT
513 // [0] u16 TOSERVER_INIT
514 // [2] u8 SER_FMT_VER_HIGHEST_READ
515 // [3] u8[20] player_name
516 // [23] u8[28] password (new in some version)
517 // [51] u16 minimum supported network protocol version (added sometime)
518 // [53] u16 maximum supported network protocol version (added later than the previous one)
519 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
520 writeU16(&data[0], TOSERVER_INIT);
521 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
523 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
524 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
526 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
529 memset((char*)&data[23], 0, PASSWORD_SIZE);
530 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
532 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
533 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
535 // Send as unreliable
536 Send(0, data, false);
539 // Not connected, return
544 Do stuff if connected
548 Run Map's timers and unload unused data
550 const float map_timer_and_unload_dtime = 5.25;
551 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
553 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
554 std::list<v3s16> deleted_blocks;
555 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
556 g_settings->getFloat("client_unload_unused_data_timeout"),
559 /*if(deleted_blocks.size() > 0)
560 infostream<<"Client: Unloaded "<<deleted_blocks.size()
561 <<" unused blocks"<<std::endl;*/
565 NOTE: This loop is intentionally iterated the way it is.
568 std::list<v3s16>::iterator i = deleted_blocks.begin();
569 std::list<v3s16> sendlist;
572 if(sendlist.size() == 255 || i == deleted_blocks.end())
574 if(sendlist.size() == 0)
583 u32 replysize = 2+1+6*sendlist.size();
584 SharedBuffer<u8> reply(replysize);
585 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
586 reply[2] = sendlist.size();
588 for(std::list<v3s16>::iterator
589 j = sendlist.begin();
590 j != sendlist.end(); ++j)
592 writeV3S16(&reply[2+1+6*k], *j);
595 m_con.Send(PEER_ID_SERVER, 1, reply, true);
597 if(i == deleted_blocks.end())
603 sendlist.push_back(*i);
613 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
615 // Control local player (0ms)
616 LocalPlayer *player = m_env.getLocalPlayer();
617 assert(player != NULL);
618 player->applyControl(dtime);
620 //TimeTaker envtimer("env step", m_device);
629 ClientEnvEvent event = m_env.getClientEvent();
630 if(event.type == CEE_NONE)
634 else if(event.type == CEE_PLAYER_DAMAGE)
636 if(m_ignore_damage_timer <= 0)
638 u8 damage = event.player_damage.amount;
640 if(event.player_damage.send_to_server)
643 // Add to ClientEvent queue
645 event.type = CE_PLAYER_DAMAGE;
646 event.player_damage.amount = damage;
647 m_client_event_queue.push_back(event);
650 else if(event.type == CEE_PLAYER_BREATH)
652 u16 breath = event.player_breath.amount;
662 float &counter = m_avg_rtt_timer;
667 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
668 // connectedAndInitialized() is true, peer exists.
669 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
670 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
675 Send player position to server
678 float &counter = m_playerpos_send_timer;
680 if(counter >= m_recommended_send_interval)
688 Replace updated meshes
691 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
693 //TimeTaker timer("** Processing mesh update result queue");
696 /*infostream<<"Mesh update result queue size is "
697 <<m_mesh_update_thread.m_queue_out.size()
700 int num_processed_meshes = 0;
701 while(!m_mesh_update_thread.m_queue_out.empty())
703 num_processed_meshes++;
704 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
705 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
708 //JMutexAutoLock lock(block->mesh_mutex);
710 // Delete the old mesh
711 if(block->mesh != NULL)
713 // TODO: Remove hardware buffers of meshbuffers of block->mesh
718 // Replace with the new mesh
719 block->mesh = r.mesh;
723 if(r.ack_block_to_server)
725 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
726 <<","<<r.p.Z<<")"<<std::endl;*/
737 u32 replysize = 2+1+6;
738 SharedBuffer<u8> reply(replysize);
739 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
741 writeV3S16(&reply[3], r.p);
743 m_con.Send(PEER_ID_SERVER, 1, reply, true);
746 if(num_processed_meshes > 0)
747 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
753 if (m_media_downloader && m_media_downloader->isStarted()) {
754 m_media_downloader->step(this);
755 if (m_media_downloader->isDone()) {
756 delete m_media_downloader;
757 m_media_downloader = NULL;
762 If the server didn't update the inventory in a while, revert
763 the local inventory (so the player notices the lag problem
764 and knows something is wrong).
766 if(m_inventory_from_server)
768 float interval = 10.0;
769 float count_before = floor(m_inventory_from_server_age / interval);
771 m_inventory_from_server_age += dtime;
773 float count_after = floor(m_inventory_from_server_age / interval);
775 if(count_after != count_before)
777 // Do this every <interval> seconds after TOCLIENT_INVENTORY
778 // Reset the locally changed inventory to the authoritative inventory
779 Player *player = m_env.getLocalPlayer();
780 player->inventory = *m_inventory_from_server;
781 m_inventory_updated = true;
786 Update positions of sounds attached to objects
789 for(std::map<int, u16>::iterator
790 i = m_sounds_to_objects.begin();
791 i != m_sounds_to_objects.end(); i++)
793 int client_id = i->first;
794 u16 object_id = i->second;
795 ClientActiveObject *cao = m_env.getActiveObject(object_id);
798 v3f pos = cao->getPosition();
799 m_sound->updateSoundPosition(client_id, pos);
804 Handle removed remotely initiated sounds
806 m_removed_sounds_check_timer += dtime;
807 if(m_removed_sounds_check_timer >= 2.32)
809 m_removed_sounds_check_timer = 0;
810 // Find removed sounds and clear references to them
811 std::set<s32> removed_server_ids;
812 for(std::map<s32, int>::iterator
813 i = m_sounds_server_to_client.begin();
814 i != m_sounds_server_to_client.end();)
816 s32 server_id = i->first;
817 int client_id = i->second;
819 if(!m_sound->soundExists(client_id)){
820 m_sounds_server_to_client.erase(server_id);
821 m_sounds_client_to_server.erase(client_id);
822 m_sounds_to_objects.erase(client_id);
823 removed_server_ids.insert(server_id);
827 if(removed_server_ids.size() != 0)
829 std::ostringstream os(std::ios_base::binary);
830 writeU16(os, TOSERVER_REMOVED_SOUNDS);
831 writeU16(os, removed_server_ids.size());
832 for(std::set<s32>::iterator i = removed_server_ids.begin();
833 i != removed_server_ids.end(); i++)
835 std::string s = os.str();
836 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
843 bool Client::loadMedia(const std::string &data, const std::string &filename)
845 // Silly irrlicht's const-incorrectness
846 Buffer<char> data_rw(data.c_str(), data.size());
850 const char *image_ext[] = {
851 ".png", ".jpg", ".bmp", ".tga",
852 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
855 name = removeStringEnd(filename, image_ext);
858 verbosestream<<"Client: Attempting to load image "
859 <<"file \""<<filename<<"\""<<std::endl;
861 io::IFileSystem *irrfs = m_device->getFileSystem();
862 video::IVideoDriver *vdrv = m_device->getVideoDriver();
864 // Create an irrlicht memory file
865 io::IReadFile *rfile = irrfs->createMemoryReadFile(
866 *data_rw, data_rw.getSize(), "_tempreadfile");
869 video::IImage *img = vdrv->createImageFromFile(rfile);
871 errorstream<<"Client: Cannot create image from data of "
872 <<"file \""<<filename<<"\""<<std::endl;
877 m_tsrc->insertSourceImage(filename, img);
884 const char *sound_ext[] = {
885 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
886 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
889 name = removeStringEnd(filename, sound_ext);
892 verbosestream<<"Client: Attempting to load sound "
893 <<"file \""<<filename<<"\""<<std::endl;
894 m_sound->loadSoundData(name, data);
898 const char *model_ext[] = {
899 ".x", ".b3d", ".md2", ".obj",
902 name = removeStringEnd(filename, model_ext);
905 verbosestream<<"Client: Storing model into Irrlicht: "
906 <<"\""<<filename<<"\""<<std::endl;
907 scene::ISceneManager *smgr = m_device->getSceneManager();
909 //check if mesh was already cached
910 scene::IAnimatedMesh *mesh =
911 smgr->getMeshCache()->getMeshByName(filename.c_str());
914 errorstream << "Multiple models with name: " << filename.c_str() <<
915 " found replacing previous model!" << std::endl;
917 smgr->getMeshCache()->removeMesh(mesh);
921 io::IFileSystem *irrfs = m_device->getFileSystem();
922 io::IReadFile *rfile = irrfs->createMemoryReadFile(
923 *data_rw, data_rw.getSize(), filename.c_str());
926 mesh = smgr->getMesh(rfile);
927 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
932 errorstream<<"Client: Don't know how to load file \""
933 <<filename<<"\""<<std::endl;
937 // Virtual methods from con::PeerHandler
938 void Client::peerAdded(con::Peer *peer)
940 infostream<<"Client::peerAdded(): peer->id="
941 <<peer->id<<std::endl;
943 void Client::deletingPeer(con::Peer *peer, bool timeout)
945 infostream<<"Client::deletingPeer(): "
946 "Server Peer is getting deleted "
947 <<"(timeout="<<timeout<<")"<<std::endl;
952 u16 number of files requested
958 void Client::request_media(const std::list<std::string> &file_requests)
960 std::ostringstream os(std::ios_base::binary);
961 writeU16(os, TOSERVER_REQUEST_MEDIA);
962 writeU16(os, file_requests.size());
964 for(std::list<std::string>::const_iterator i = file_requests.begin();
965 i != file_requests.end(); ++i) {
966 os<<serializeString(*i);
970 std::string s = os.str();
971 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
974 infostream<<"Client: Sending media request list to server ("
975 <<file_requests.size()<<" files)"<<std::endl;
978 void Client::received_media()
980 // notify server we received everything
981 std::ostringstream os(std::ios_base::binary);
982 writeU16(os, TOSERVER_RECEIVED_MEDIA);
983 std::string s = os.str();
984 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
987 infostream<<"Client: Notifying server that we received all media"
991 void Client::ReceiveAll()
993 DSTACK(__FUNCTION_NAME);
994 u32 start_ms = porting::getTimeMs();
997 // Limit time even if there would be huge amounts of data to
999 if(porting::getTimeMs() > start_ms + 100)
1004 g_profiler->graphAdd("client_received_packets", 1);
1006 catch(con::NoIncomingDataException &e)
1010 catch(con::InvalidIncomingDataException &e)
1012 infostream<<"Client::ReceiveAll(): "
1013 "InvalidIncomingDataException: what()="
1014 <<e.what()<<std::endl;
1019 void Client::Receive()
1021 DSTACK(__FUNCTION_NAME);
1022 SharedBuffer<u8> data;
1026 //TimeTaker t1("con mutex and receive", m_device);
1027 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1028 datasize = m_con.Receive(sender_peer_id, data);
1030 //TimeTaker t1("ProcessData", m_device);
1031 ProcessData(*data, datasize, sender_peer_id);
1035 sender_peer_id given to this shall be quaranteed to be a valid peer
1037 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1039 DSTACK(__FUNCTION_NAME);
1041 // Ignore packets that don't even fit a command
1044 m_packetcounter.add(60000);
1048 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1050 //infostream<<"Client: received command="<<command<<std::endl;
1051 m_packetcounter.add((u16)command);
1054 If this check is removed, be sure to change the queue
1055 system to know the ids
1057 if(sender_peer_id != PEER_ID_SERVER)
1059 infostream<<"Client::ProcessData(): Discarding data not "
1060 "coming from server: peer_id="<<sender_peer_id
1065 u8 ser_version = m_server_ser_ver;
1067 //infostream<<"Client received command="<<(int)command<<std::endl;
1069 if(command == TOCLIENT_INIT)
1074 u8 deployed = data[2];
1076 infostream<<"Client: TOCLIENT_INIT received with "
1077 "deployed="<<((int)deployed&0xff)<<std::endl;
1079 if(!ser_ver_supported(deployed))
1081 infostream<<"Client: TOCLIENT_INIT: Server sent "
1082 <<"unsupported ser_fmt_ver"<<std::endl;
1086 m_server_ser_ver = deployed;
1088 // Get player position
1089 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1090 if(datasize >= 2+1+6)
1091 playerpos_s16 = readV3S16(&data[2+1]);
1092 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1095 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1097 // Set player position
1098 Player *player = m_env.getLocalPlayer();
1099 assert(player != NULL);
1100 player->setPosition(playerpos_f);
1103 if(datasize >= 2+1+6+8)
1106 m_map_seed = readU64(&data[2+1+6]);
1107 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1110 if(datasize >= 2+1+6+8+4)
1113 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1114 infostream<<"Client: received recommended send interval "
1115 <<m_recommended_send_interval<<std::endl;
1120 SharedBuffer<u8> reply(replysize);
1121 writeU16(&reply[0], TOSERVER_INIT2);
1123 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1128 if(command == TOCLIENT_ACCESS_DENIED)
1130 // The server didn't like our password. Note, this needs
1131 // to be processed even if the serialisation format has
1132 // not been agreed yet, the same as TOCLIENT_INIT.
1133 m_access_denied = true;
1134 m_access_denied_reason = L"Unknown";
1137 std::string datastring((char*)&data[2], datasize-2);
1138 std::istringstream is(datastring, std::ios_base::binary);
1139 m_access_denied_reason = deSerializeWideString(is);
1144 if(ser_version == SER_FMT_VER_INVALID)
1146 infostream<<"Client: Server serialization"
1147 " format invalid or not initialized."
1148 " Skipping incoming command="<<command<<std::endl;
1152 // Just here to avoid putting the two if's together when
1153 // making some copypasta
1156 if(command == TOCLIENT_REMOVENODE)
1161 p.X = readS16(&data[2]);
1162 p.Y = readS16(&data[4]);
1163 p.Z = readS16(&data[6]);
1165 //TimeTaker t1("TOCLIENT_REMOVENODE");
1169 else if(command == TOCLIENT_ADDNODE)
1171 if(datasize < 8 + MapNode::serializedLength(ser_version))
1175 p.X = readS16(&data[2]);
1176 p.Y = readS16(&data[4]);
1177 p.Z = readS16(&data[6]);
1179 //TimeTaker t1("TOCLIENT_ADDNODE");
1182 n.deSerialize(&data[8], ser_version);
1184 bool remove_metadata = true;
1185 u32 index = 8 + MapNode::serializedLength(ser_version);
1186 if ((datasize >= index+1) && data[index]){
1187 remove_metadata = false;
1190 addNode(p, n, remove_metadata);
1192 else if(command == TOCLIENT_BLOCKDATA)
1194 // Ignore too small packet
1199 p.X = readS16(&data[2]);
1200 p.Y = readS16(&data[4]);
1201 p.Z = readS16(&data[6]);
1203 /*infostream<<"Client: Thread: BLOCKDATA for ("
1204 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1205 /*infostream<<"Client: Thread: BLOCKDATA for ("
1206 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1208 std::string datastring((char*)&data[8], datasize-8);
1209 std::istringstream istr(datastring, std::ios_base::binary);
1214 v2s16 p2d(p.X, p.Z);
1215 sector = m_env.getMap().emergeSector(p2d);
1217 assert(sector->getPos() == p2d);
1219 //TimeTaker timer("MapBlock deSerialize");
1222 block = sector->getBlockNoCreateNoEx(p.Y);
1226 Update an existing block
1228 //infostream<<"Updating"<<std::endl;
1229 block->deSerialize(istr, ser_version, false);
1230 block->deSerializeNetworkSpecific(istr);
1237 //infostream<<"Creating new"<<std::endl;
1238 block = new MapBlock(&m_env.getMap(), p, this);
1239 block->deSerialize(istr, ser_version, false);
1240 block->deSerializeNetworkSpecific(istr);
1241 sector->insertBlock(block);
1255 u32 replysize = 2+1+6;
1256 SharedBuffer<u8> reply(replysize);
1257 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1259 writeV3S16(&reply[3], p);
1261 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1265 Add it to mesh update queue and set it to be acknowledged after update.
1267 //infostream<<"Adding mesh update task for received block"<<std::endl;
1268 addUpdateMeshTaskWithEdge(p, true);
1270 else if(command == TOCLIENT_INVENTORY)
1275 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1278 //TimeTaker t2("mutex locking", m_device);
1279 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1282 //TimeTaker t3("istringstream init", m_device);
1283 std::string datastring((char*)&data[2], datasize-2);
1284 std::istringstream is(datastring, std::ios_base::binary);
1287 //TimeTaker t4("player get", m_device);
1288 Player *player = m_env.getLocalPlayer();
1289 assert(player != NULL);
1292 //TimeTaker t1("inventory.deSerialize()", m_device);
1293 player->inventory.deSerialize(is);
1296 m_inventory_updated = true;
1298 delete m_inventory_from_server;
1299 m_inventory_from_server = new Inventory(player->inventory);
1300 m_inventory_from_server_age = 0.0;
1302 //infostream<<"Client got player inventory:"<<std::endl;
1303 //player->inventory.print(infostream);
1306 else if(command == TOCLIENT_TIME_OF_DAY)
1311 u16 time_of_day = readU16(&data[2]);
1312 time_of_day = time_of_day % 24000;
1313 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1314 float time_speed = 0;
1315 if(datasize >= 2 + 2 + 4){
1316 time_speed = readF1000(&data[4]);
1318 // Old message; try to approximate speed of time by ourselves
1319 float time_of_day_f = (float)time_of_day / 24000.0;
1320 float tod_diff_f = 0;
1321 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1322 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1324 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1325 m_last_time_of_day_f = time_of_day_f;
1326 float time_diff = m_time_of_day_update_timer;
1327 m_time_of_day_update_timer = 0;
1328 if(m_time_of_day_set){
1329 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1330 infostream<<"Client: Measured time_of_day speed (old format): "
1331 <<time_speed<<" tod_diff_f="<<tod_diff_f
1332 <<" time_diff="<<time_diff<<std::endl;
1336 // Update environment
1337 m_env.setTimeOfDay(time_of_day);
1338 m_env.setTimeOfDaySpeed(time_speed);
1339 m_time_of_day_set = true;
1341 u32 dr = m_env.getDayNightRatio();
1342 verbosestream<<"Client: time_of_day="<<time_of_day
1343 <<" time_speed="<<time_speed
1344 <<" dr="<<dr<<std::endl;
1346 else if(command == TOCLIENT_CHAT_MESSAGE)
1354 std::string datastring((char*)&data[2], datasize-2);
1355 std::istringstream is(datastring, std::ios_base::binary);
1358 is.read((char*)buf, 2);
1359 u16 len = readU16(buf);
1361 std::wstring message;
1362 for(u16 i=0; i<len; i++)
1364 is.read((char*)buf, 2);
1365 message += (wchar_t)readU16(buf);
1368 /*infostream<<"Client received chat message: "
1369 <<wide_to_narrow(message)<<std::endl;*/
1371 m_chat_queue.push_back(message);
1373 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1375 //if(g_settings->getBool("enable_experimental"))
1379 u16 count of removed objects
1380 for all removed objects {
1383 u16 count of added objects
1384 for all added objects {
1387 u32 initialization data length
1388 string initialization data
1393 // Get all data except the command number
1394 std::string datastring((char*)&data[2], datasize-2);
1395 // Throw them in an istringstream
1396 std::istringstream is(datastring, std::ios_base::binary);
1400 // Read removed objects
1402 u16 removed_count = readU16((u8*)buf);
1403 for(u16 i=0; i<removed_count; i++)
1406 u16 id = readU16((u8*)buf);
1409 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1410 m_env.removeActiveObject(id);
1414 // Read added objects
1416 u16 added_count = readU16((u8*)buf);
1417 for(u16 i=0; i<added_count; i++)
1420 u16 id = readU16((u8*)buf);
1422 u8 type = readU8((u8*)buf);
1423 std::string data = deSerializeLongString(is);
1426 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1427 m_env.addActiveObject(id, type, data);
1432 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1434 //if(g_settings->getBool("enable_experimental"))
1446 // Get all data except the command number
1447 std::string datastring((char*)&data[2], datasize-2);
1448 // Throw them in an istringstream
1449 std::istringstream is(datastring, std::ios_base::binary);
1451 while(is.eof() == false)
1455 u16 id = readU16((u8*)buf);
1459 u16 message_size = readU16((u8*)buf);
1460 std::string message;
1461 message.reserve(message_size);
1462 for(u16 i=0; i<message_size; i++)
1465 message.append(buf, 1);
1467 // Pass on to the environment
1469 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1470 m_env.processActiveObjectMessage(id, message);
1475 else if(command == TOCLIENT_MOVEMENT)
1477 std::string datastring((char*)&data[2], datasize-2);
1478 std::istringstream is(datastring, std::ios_base::binary);
1479 Player *player = m_env.getLocalPlayer();
1480 assert(player != NULL);
1482 player->movement_acceleration_default = readF1000(is) * BS;
1483 player->movement_acceleration_air = readF1000(is) * BS;
1484 player->movement_acceleration_fast = readF1000(is) * BS;
1485 player->movement_speed_walk = readF1000(is) * BS;
1486 player->movement_speed_crouch = readF1000(is) * BS;
1487 player->movement_speed_fast = readF1000(is) * BS;
1488 player->movement_speed_climb = readF1000(is) * BS;
1489 player->movement_speed_jump = readF1000(is) * BS;
1490 player->movement_liquid_fluidity = readF1000(is) * BS;
1491 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1492 player->movement_liquid_sink = readF1000(is) * BS;
1493 player->movement_gravity = readF1000(is) * BS;
1495 else if(command == TOCLIENT_HP)
1497 std::string datastring((char*)&data[2], datasize-2);
1498 std::istringstream is(datastring, std::ios_base::binary);
1499 Player *player = m_env.getLocalPlayer();
1500 assert(player != NULL);
1501 u8 oldhp = player->hp;
1507 // Add to ClientEvent queue
1509 event.type = CE_PLAYER_DAMAGE;
1510 event.player_damage.amount = oldhp - hp;
1511 m_client_event_queue.push_back(event);
1514 else if(command == TOCLIENT_BREATH)
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 u16 breath = readU16(is);
1521 player->setBreath(breath) ;
1523 else if(command == TOCLIENT_MOVE_PLAYER)
1525 std::string datastring((char*)&data[2], datasize-2);
1526 std::istringstream is(datastring, std::ios_base::binary);
1527 Player *player = m_env.getLocalPlayer();
1528 assert(player != NULL);
1529 v3f pos = readV3F1000(is);
1530 f32 pitch = readF1000(is);
1531 f32 yaw = readF1000(is);
1532 player->setPosition(pos);
1533 /*player->setPitch(pitch);
1534 player->setYaw(yaw);*/
1536 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1537 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1543 Add to ClientEvent queue.
1544 This has to be sent to the main program because otherwise
1545 it would just force the pitch and yaw values to whatever
1546 the camera points to.
1549 event.type = CE_PLAYER_FORCE_MOVE;
1550 event.player_force_move.pitch = pitch;
1551 event.player_force_move.yaw = yaw;
1552 m_client_event_queue.push_back(event);
1554 // Ignore damage for a few seconds, so that the player doesn't
1555 // get damage from falling on ground
1556 m_ignore_damage_timer = 3.0;
1558 else if(command == TOCLIENT_PLAYERITEM)
1560 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1562 else if(command == TOCLIENT_DEATHSCREEN)
1564 std::string datastring((char*)&data[2], datasize-2);
1565 std::istringstream is(datastring, std::ios_base::binary);
1567 bool set_camera_point_target = readU8(is);
1568 v3f camera_point_target = readV3F1000(is);
1571 event.type = CE_DEATHSCREEN;
1572 event.deathscreen.set_camera_point_target = set_camera_point_target;
1573 event.deathscreen.camera_point_target_x = camera_point_target.X;
1574 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1575 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1576 m_client_event_queue.push_back(event);
1578 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1580 std::string datastring((char*)&data[2], datasize-2);
1581 std::istringstream is(datastring, std::ios_base::binary);
1583 int num_files = readU16(is);
1585 infostream<<"Client: Received media announcement: packet size: "
1586 <<datasize<<std::endl;
1588 if (m_media_downloader == NULL ||
1589 m_media_downloader->isStarted()) {
1590 const char *problem = m_media_downloader ?
1591 "we already saw another announcement" :
1592 "all media has been received already";
1593 errorstream<<"Client: Received media announcement but "
1595 <<" files="<<num_files
1596 <<" size="<<datasize<<std::endl;
1600 // Mesh update thread must be stopped while
1601 // updating content definitions
1602 assert(!m_mesh_update_thread.IsRunning());
1604 for(int i=0; i<num_files; i++)
1606 std::string name = deSerializeString(is);
1607 std::string sha1_base64 = deSerializeString(is);
1608 std::string sha1_raw = base64_decode(sha1_base64);
1609 m_media_downloader->addFile(name, sha1_raw);
1612 std::vector<std::string> remote_media;
1614 Strfnd sf(deSerializeString(is));
1615 while(!sf.atend()) {
1616 std::string baseurl = trim(sf.next(","));
1618 m_media_downloader->addRemoteServer(baseurl);
1621 catch(SerializationError) {
1622 // not supported by server or turned off
1625 m_media_downloader->step(this);
1626 if (m_media_downloader->isDone()) {
1627 // might be done already if all media is in the cache
1628 delete m_media_downloader;
1629 m_media_downloader = NULL;
1632 else if(command == TOCLIENT_MEDIA)
1634 std::string datastring((char*)&data[2], datasize-2);
1635 std::istringstream is(datastring, std::ios_base::binary);
1639 u16 total number of file bunches
1640 u16 index of this bunch
1641 u32 number of files in this bunch
1649 int num_bunches = readU16(is);
1650 int bunch_i = readU16(is);
1651 u32 num_files = readU32(is);
1652 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1653 <<num_bunches<<" files="<<num_files
1654 <<" size="<<datasize<<std::endl;
1659 if (m_media_downloader == NULL ||
1660 !m_media_downloader->isStarted()) {
1661 const char *problem = m_media_downloader ?
1662 "media has not been requested" :
1663 "all media has been received already";
1664 errorstream<<"Client: Received media but "
1666 <<" bunch "<<bunch_i<<"/"<<num_bunches
1667 <<" files="<<num_files
1668 <<" size="<<datasize<<std::endl;
1672 // Mesh update thread must be stopped while
1673 // updating content definitions
1674 assert(!m_mesh_update_thread.IsRunning());
1676 for(u32 i=0; i<num_files; i++){
1677 std::string name = deSerializeString(is);
1678 std::string data = deSerializeLongString(is);
1679 m_media_downloader->conventionalTransferDone(
1683 if (m_media_downloader->isDone()) {
1684 delete m_media_downloader;
1685 m_media_downloader = NULL;
1688 else if(command == TOCLIENT_TOOLDEF)
1690 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1692 else if(command == TOCLIENT_NODEDEF)
1694 infostream<<"Client: Received node definitions: packet size: "
1695 <<datasize<<std::endl;
1697 // Mesh update thread must be stopped while
1698 // updating content definitions
1699 assert(!m_mesh_update_thread.IsRunning());
1701 // Decompress node definitions
1702 std::string datastring((char*)&data[2], datasize-2);
1703 std::istringstream is(datastring, std::ios_base::binary);
1704 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1705 std::ostringstream tmp_os;
1706 decompressZlib(tmp_is, tmp_os);
1708 // Deserialize node definitions
1709 std::istringstream tmp_is2(tmp_os.str());
1710 m_nodedef->deSerialize(tmp_is2);
1711 m_nodedef_received = true;
1713 else if(command == TOCLIENT_CRAFTITEMDEF)
1715 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1717 else if(command == TOCLIENT_ITEMDEF)
1719 infostream<<"Client: Received item definitions: packet size: "
1720 <<datasize<<std::endl;
1722 // Mesh update thread must be stopped while
1723 // updating content definitions
1724 assert(!m_mesh_update_thread.IsRunning());
1726 // Decompress item definitions
1727 std::string datastring((char*)&data[2], datasize-2);
1728 std::istringstream is(datastring, std::ios_base::binary);
1729 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1730 std::ostringstream tmp_os;
1731 decompressZlib(tmp_is, tmp_os);
1733 // Deserialize node definitions
1734 std::istringstream tmp_is2(tmp_os.str());
1735 m_itemdef->deSerialize(tmp_is2);
1736 m_itemdef_received = true;
1738 else if(command == TOCLIENT_PLAY_SOUND)
1740 std::string datastring((char*)&data[2], datasize-2);
1741 std::istringstream is(datastring, std::ios_base::binary);
1743 s32 server_id = readS32(is);
1744 std::string name = deSerializeString(is);
1745 float gain = readF1000(is);
1746 int type = readU8(is); // 0=local, 1=positional, 2=object
1747 v3f pos = readV3F1000(is);
1748 u16 object_id = readU16(is);
1749 bool loop = readU8(is);
1754 client_id = m_sound->playSound(name, loop, gain);
1756 case 1: // positional
1757 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1760 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1762 pos = cao->getPosition();
1763 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1764 // TODO: Set up sound to move with object
1769 if(client_id != -1){
1770 m_sounds_server_to_client[server_id] = client_id;
1771 m_sounds_client_to_server[client_id] = server_id;
1773 m_sounds_to_objects[client_id] = object_id;
1776 else if(command == TOCLIENT_STOP_SOUND)
1778 std::string datastring((char*)&data[2], datasize-2);
1779 std::istringstream is(datastring, std::ios_base::binary);
1781 s32 server_id = readS32(is);
1782 std::map<s32, int>::iterator i =
1783 m_sounds_server_to_client.find(server_id);
1784 if(i != m_sounds_server_to_client.end()){
1785 int client_id = i->second;
1786 m_sound->stopSound(client_id);
1789 else if(command == TOCLIENT_PRIVILEGES)
1791 std::string datastring((char*)&data[2], datasize-2);
1792 std::istringstream is(datastring, std::ios_base::binary);
1794 m_privileges.clear();
1795 infostream<<"Client: Privileges updated: ";
1796 u16 num_privileges = readU16(is);
1797 for(u16 i=0; i<num_privileges; i++){
1798 std::string priv = deSerializeString(is);
1799 m_privileges.insert(priv);
1800 infostream<<priv<<" ";
1802 infostream<<std::endl;
1804 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1806 std::string datastring((char*)&data[2], datasize-2);
1807 std::istringstream is(datastring, std::ios_base::binary);
1809 // Store formspec in LocalPlayer
1810 Player *player = m_env.getLocalPlayer();
1811 assert(player != NULL);
1812 player->inventory_formspec = deSerializeLongString(is);
1814 else if(command == TOCLIENT_DETACHED_INVENTORY)
1816 std::string datastring((char*)&data[2], datasize-2);
1817 std::istringstream is(datastring, std::ios_base::binary);
1819 std::string name = deSerializeString(is);
1821 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1823 Inventory *inv = NULL;
1824 if(m_detached_inventories.count(name) > 0)
1825 inv = m_detached_inventories[name];
1827 inv = new Inventory(m_itemdef);
1828 m_detached_inventories[name] = inv;
1830 inv->deSerialize(is);
1832 else if(command == TOCLIENT_SHOW_FORMSPEC)
1834 std::string datastring((char*)&data[2], datasize-2);
1835 std::istringstream is(datastring, std::ios_base::binary);
1837 std::string formspec = deSerializeLongString(is);
1838 std::string formname = deSerializeString(is);
1841 event.type = CE_SHOW_FORMSPEC;
1842 // pointer is required as event is a struct only!
1843 // adding a std:string to a struct isn't possible
1844 event.show_formspec.formspec = new std::string(formspec);
1845 event.show_formspec.formname = new std::string(formname);
1846 m_client_event_queue.push_back(event);
1848 else if(command == TOCLIENT_SPAWN_PARTICLE)
1850 std::string datastring((char*)&data[2], datasize-2);
1851 std::istringstream is(datastring, std::ios_base::binary);
1853 v3f pos = readV3F1000(is);
1854 v3f vel = readV3F1000(is);
1855 v3f acc = readV3F1000(is);
1856 float expirationtime = readF1000(is);
1857 float size = readF1000(is);
1858 bool collisiondetection = readU8(is);
1859 std::string texture = deSerializeLongString(is);
1862 event.type = CE_SPAWN_PARTICLE;
1863 event.spawn_particle.pos = new v3f (pos);
1864 event.spawn_particle.vel = new v3f (vel);
1865 event.spawn_particle.acc = new v3f (acc);
1867 event.spawn_particle.expirationtime = expirationtime;
1868 event.spawn_particle.size = size;
1869 event.spawn_particle.collisiondetection =
1871 event.spawn_particle.texture = new std::string(texture);
1873 m_client_event_queue.push_back(event);
1875 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1877 std::string datastring((char*)&data[2], datasize-2);
1878 std::istringstream is(datastring, std::ios_base::binary);
1880 u16 amount = readU16(is);
1881 float spawntime = readF1000(is);
1882 v3f minpos = readV3F1000(is);
1883 v3f maxpos = readV3F1000(is);
1884 v3f minvel = readV3F1000(is);
1885 v3f maxvel = readV3F1000(is);
1886 v3f minacc = readV3F1000(is);
1887 v3f maxacc = readV3F1000(is);
1888 float minexptime = readF1000(is);
1889 float maxexptime = readF1000(is);
1890 float minsize = readF1000(is);
1891 float maxsize = readF1000(is);
1892 bool collisiondetection = readU8(is);
1893 std::string texture = deSerializeLongString(is);
1894 u32 id = readU32(is);
1897 event.type = CE_ADD_PARTICLESPAWNER;
1898 event.add_particlespawner.amount = amount;
1899 event.add_particlespawner.spawntime = spawntime;
1901 event.add_particlespawner.minpos = new v3f (minpos);
1902 event.add_particlespawner.maxpos = new v3f (maxpos);
1903 event.add_particlespawner.minvel = new v3f (minvel);
1904 event.add_particlespawner.maxvel = new v3f (maxvel);
1905 event.add_particlespawner.minacc = new v3f (minacc);
1906 event.add_particlespawner.maxacc = new v3f (maxacc);
1908 event.add_particlespawner.minexptime = minexptime;
1909 event.add_particlespawner.maxexptime = maxexptime;
1910 event.add_particlespawner.minsize = minsize;
1911 event.add_particlespawner.maxsize = maxsize;
1912 event.add_particlespawner.collisiondetection = collisiondetection;
1913 event.add_particlespawner.texture = new std::string(texture);
1914 event.add_particlespawner.id = id;
1916 m_client_event_queue.push_back(event);
1918 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
1920 std::string datastring((char*)&data[2], datasize-2);
1921 std::istringstream is(datastring, std::ios_base::binary);
1923 u32 id = readU16(is);
1926 event.type = CE_DELETE_PARTICLESPAWNER;
1927 event.delete_particlespawner.id = id;
1929 m_client_event_queue.push_back(event);
1931 else if(command == TOCLIENT_HUDADD)
1933 std::string datastring((char *)&data[2], datasize - 2);
1934 std::istringstream is(datastring, std::ios_base::binary);
1936 u32 id = readU32(is);
1937 u8 type = readU8(is);
1938 v2f pos = readV2F1000(is);
1939 std::string name = deSerializeString(is);
1940 v2f scale = readV2F1000(is);
1941 std::string text = deSerializeString(is);
1942 u32 number = readU32(is);
1943 u32 item = readU32(is);
1944 u32 dir = readU32(is);
1945 v2f align = readV2F1000(is);
1946 v2f offset = readV2F1000(is);
1949 event.type = CE_HUDADD;
1950 event.hudadd.id = id;
1951 event.hudadd.type = type;
1952 event.hudadd.pos = new v2f(pos);
1953 event.hudadd.name = new std::string(name);
1954 event.hudadd.scale = new v2f(scale);
1955 event.hudadd.text = new std::string(text);
1956 event.hudadd.number = number;
1957 event.hudadd.item = item;
1958 event.hudadd.dir = dir;
1959 event.hudadd.align = new v2f(align);
1960 event.hudadd.offset = new v2f(offset);
1961 m_client_event_queue.push_back(event);
1963 else if(command == TOCLIENT_HUDRM)
1965 std::string datastring((char *)&data[2], datasize - 2);
1966 std::istringstream is(datastring, std::ios_base::binary);
1968 u32 id = readU32(is);
1971 event.type = CE_HUDRM;
1972 event.hudrm.id = id;
1973 m_client_event_queue.push_back(event);
1975 else if(command == TOCLIENT_HUDCHANGE)
1981 std::string datastring((char *)&data[2], datasize - 2);
1982 std::istringstream is(datastring, std::ios_base::binary);
1984 u32 id = readU32(is);
1985 u8 stat = (HudElementStat)readU8(is);
1987 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1988 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1989 v2fdata = readV2F1000(is);
1990 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1991 sdata = deSerializeString(is);
1993 intdata = readU32(is);
1996 event.type = CE_HUDCHANGE;
1997 event.hudchange.id = id;
1998 event.hudchange.stat = (HudElementStat)stat;
1999 event.hudchange.v2fdata = new v2f(v2fdata);
2000 event.hudchange.sdata = new std::string(sdata);
2001 event.hudchange.data = intdata;
2002 m_client_event_queue.push_back(event);
2004 else if(command == TOCLIENT_HUD_SET_FLAGS)
2006 std::string datastring((char *)&data[2], datasize - 2);
2007 std::istringstream is(datastring, std::ios_base::binary);
2009 Player *player = m_env.getLocalPlayer();
2010 assert(player != NULL);
2012 u32 flags = readU32(is);
2013 u32 mask = readU32(is);
2015 player->hud_flags &= ~mask;
2016 player->hud_flags |= flags;
2018 else if(command == TOCLIENT_HUD_SET_PARAM)
2020 std::string datastring((char *)&data[2], datasize - 2);
2021 std::istringstream is(datastring, std::ios_base::binary);
2023 Player *player = m_env.getLocalPlayer();
2024 assert(player != NULL);
2026 u16 param = readU16(is);
2027 std::string value = deSerializeString(is);
2029 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2030 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2031 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2032 player->hud_hotbar_itemcount = hotbar_itemcount;
2033 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2034 ((LocalPlayer *) player)->hotbar_image = value;
2035 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2036 ((LocalPlayer *) player)->hotbar_selected_image = value;
2041 infostream<<"Client: Ignoring unknown command "
2042 <<command<<std::endl;
2046 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2048 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2049 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2052 void Client::interact(u8 action, const PointedThing& pointed)
2054 if(connectedAndInitialized() == false){
2055 infostream<<"Client::interact() "
2056 "cancelled (not connected)"
2061 std::ostringstream os(std::ios_base::binary);
2067 [5] u32 length of the next item
2068 [9] serialized PointedThing
2070 0: start digging (from undersurface) or use
2071 1: stop digging (all parameters ignored)
2072 2: digging completed
2073 3: place block or item (to abovesurface)
2076 writeU16(os, TOSERVER_INTERACT);
2077 writeU8(os, action);
2078 writeU16(os, getPlayerItem());
2079 std::ostringstream tmp_os(std::ios::binary);
2080 pointed.serialize(tmp_os);
2081 os<<serializeLongString(tmp_os.str());
2083 std::string s = os.str();
2084 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2087 Send(0, data, true);
2090 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2091 const std::map<std::string, std::string> &fields)
2093 std::ostringstream os(std::ios_base::binary);
2095 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2097 os<<serializeString(formname);
2098 writeU16(os, fields.size());
2099 for(std::map<std::string, std::string>::const_iterator
2100 i = fields.begin(); i != fields.end(); i++){
2101 const std::string &name = i->first;
2102 const std::string &value = i->second;
2103 os<<serializeString(name);
2104 os<<serializeLongString(value);
2108 std::string s = os.str();
2109 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2111 Send(0, data, true);
2114 void Client::sendInventoryFields(const std::string &formname,
2115 const std::map<std::string, std::string> &fields)
2117 std::ostringstream os(std::ios_base::binary);
2119 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2120 os<<serializeString(formname);
2121 writeU16(os, fields.size());
2122 for(std::map<std::string, std::string>::const_iterator
2123 i = fields.begin(); i != fields.end(); i++){
2124 const std::string &name = i->first;
2125 const std::string &value = i->second;
2126 os<<serializeString(name);
2127 os<<serializeLongString(value);
2131 std::string s = os.str();
2132 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2134 Send(0, data, true);
2137 void Client::sendInventoryAction(InventoryAction *a)
2139 std::ostringstream os(std::ios_base::binary);
2143 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2144 os.write((char*)buf, 2);
2149 std::string s = os.str();
2150 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2152 Send(0, data, true);
2155 void Client::sendChatMessage(const std::wstring &message)
2157 std::ostringstream os(std::ios_base::binary);
2161 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2162 os.write((char*)buf, 2);
2165 writeU16(buf, message.size());
2166 os.write((char*)buf, 2);
2169 for(u32 i=0; i<message.size(); i++)
2173 os.write((char*)buf, 2);
2177 std::string s = os.str();
2178 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2180 Send(0, data, true);
2183 void Client::sendChangePassword(const std::wstring oldpassword,
2184 const std::wstring newpassword)
2186 Player *player = m_env.getLocalPlayer();
2190 std::string playername = player->getName();
2191 std::string oldpwd = translatePassword(playername, oldpassword);
2192 std::string newpwd = translatePassword(playername, newpassword);
2194 std::ostringstream os(std::ios_base::binary);
2195 u8 buf[2+PASSWORD_SIZE*2];
2197 [0] u16 TOSERVER_PASSWORD
2198 [2] u8[28] old password
2199 [30] u8[28] new password
2202 writeU16(buf, TOSERVER_PASSWORD);
2203 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2205 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2206 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2208 buf[2+PASSWORD_SIZE-1] = 0;
2209 buf[30+PASSWORD_SIZE-1] = 0;
2210 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2213 std::string s = os.str();
2214 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2216 Send(0, data, true);
2220 void Client::sendDamage(u8 damage)
2222 DSTACK(__FUNCTION_NAME);
2223 std::ostringstream os(std::ios_base::binary);
2225 writeU16(os, TOSERVER_DAMAGE);
2226 writeU8(os, damage);
2229 std::string s = os.str();
2230 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2232 Send(0, data, true);
2235 void Client::sendBreath(u16 breath)
2237 DSTACK(__FUNCTION_NAME);
2238 std::ostringstream os(std::ios_base::binary);
2240 writeU16(os, TOSERVER_BREATH);
2241 writeU16(os, breath);
2243 std::string s = os.str();
2244 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2246 Send(0, data, true);
2249 void Client::sendRespawn()
2251 DSTACK(__FUNCTION_NAME);
2252 std::ostringstream os(std::ios_base::binary);
2254 writeU16(os, TOSERVER_RESPAWN);
2257 std::string s = os.str();
2258 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2260 Send(0, data, true);
2263 void Client::sendPlayerPos()
2265 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2267 LocalPlayer *myplayer = m_env.getLocalPlayer();
2268 if(myplayer == NULL)
2271 // Save bandwidth by only updating position when something changed
2272 if(myplayer->last_position == myplayer->getPosition() &&
2273 myplayer->last_speed == myplayer->getSpeed() &&
2274 myplayer->last_pitch == myplayer->getPitch() &&
2275 myplayer->last_yaw == myplayer->getYaw() &&
2276 myplayer->last_keyPressed == myplayer->keyPressed)
2279 myplayer->last_position = myplayer->getPosition();
2280 myplayer->last_speed = myplayer->getSpeed();
2281 myplayer->last_pitch = myplayer->getPitch();
2282 myplayer->last_yaw = myplayer->getYaw();
2283 myplayer->last_keyPressed = myplayer->keyPressed;
2287 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2288 our_peer_id = m_con.GetPeerID();
2291 // Set peer id if not set already
2292 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2293 myplayer->peer_id = our_peer_id;
2294 // Check that an existing peer_id is the same as the connection's
2295 assert(myplayer->peer_id == our_peer_id);
2297 v3f pf = myplayer->getPosition();
2298 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2299 v3f sf = myplayer->getSpeed();
2300 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2301 s32 pitch = myplayer->getPitch() * 100;
2302 s32 yaw = myplayer->getYaw() * 100;
2303 u32 keyPressed=myplayer->keyPressed;
2307 [2] v3s32 position*100
2308 [2+12] v3s32 speed*100
2309 [2+12+12] s32 pitch*100
2310 [2+12+12+4] s32 yaw*100
2311 [2+12+12+4+4] u32 keyPressed
2313 SharedBuffer<u8> data(2+12+12+4+4+4);
2314 writeU16(&data[0], TOSERVER_PLAYERPOS);
2315 writeV3S32(&data[2], position);
2316 writeV3S32(&data[2+12], speed);
2317 writeS32(&data[2+12+12], pitch);
2318 writeS32(&data[2+12+12+4], yaw);
2319 writeU32(&data[2+12+12+4+4], keyPressed);
2320 // Send as unreliable
2321 Send(0, data, false);
2324 void Client::sendPlayerItem(u16 item)
2326 Player *myplayer = m_env.getLocalPlayer();
2327 if(myplayer == NULL)
2330 u16 our_peer_id = m_con.GetPeerID();
2332 // Set peer id if not set already
2333 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2334 myplayer->peer_id = our_peer_id;
2335 // Check that an existing peer_id is the same as the connection's
2336 assert(myplayer->peer_id == our_peer_id);
2338 SharedBuffer<u8> data(2+2);
2339 writeU16(&data[0], TOSERVER_PLAYERITEM);
2340 writeU16(&data[2], item);
2343 Send(0, data, true);
2346 void Client::removeNode(v3s16 p)
2348 std::map<v3s16, MapBlock*> modified_blocks;
2352 //TimeTaker t("removeNodeAndUpdate", m_device);
2353 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2355 catch(InvalidPositionException &e)
2359 // add urgent task to update the modified node
2360 addUpdateMeshTaskForNode(p, false, true);
2362 for(std::map<v3s16, MapBlock * >::iterator
2363 i = modified_blocks.begin();
2364 i != modified_blocks.end(); ++i)
2366 addUpdateMeshTaskWithEdge(i->first);
2370 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2372 TimeTaker timer1("Client::addNode()");
2374 std::map<v3s16, MapBlock*> modified_blocks;
2378 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2379 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2381 catch(InvalidPositionException &e)
2384 for(std::map<v3s16, MapBlock * >::iterator
2385 i = modified_blocks.begin();
2386 i != modified_blocks.end(); ++i)
2388 addUpdateMeshTaskWithEdge(i->first);
2392 void Client::setPlayerControl(PlayerControl &control)
2394 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2395 LocalPlayer *player = m_env.getLocalPlayer();
2396 assert(player != NULL);
2397 player->control = control;
2400 void Client::selectPlayerItem(u16 item)
2402 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2403 m_playeritem = item;
2404 m_inventory_updated = true;
2405 sendPlayerItem(item);
2408 // Returns true if the inventory of the local player has been
2409 // updated from the server. If it is true, it is set to false.
2410 bool Client::getLocalInventoryUpdated()
2412 // m_inventory_updated is behind envlock
2413 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2414 bool updated = m_inventory_updated;
2415 m_inventory_updated = false;
2419 // Copies the inventory of the local player to parameter
2420 void Client::getLocalInventory(Inventory &dst)
2422 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2423 Player *player = m_env.getLocalPlayer();
2424 assert(player != NULL);
2425 dst = player->inventory;
2428 Inventory* Client::getInventory(const InventoryLocation &loc)
2431 case InventoryLocation::UNDEFINED:
2434 case InventoryLocation::CURRENT_PLAYER:
2436 Player *player = m_env.getLocalPlayer();
2437 assert(player != NULL);
2438 return &player->inventory;
2441 case InventoryLocation::PLAYER:
2443 Player *player = m_env.getPlayer(loc.name.c_str());
2446 return &player->inventory;
2449 case InventoryLocation::NODEMETA:
2451 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2454 return meta->getInventory();
2457 case InventoryLocation::DETACHED:
2459 if(m_detached_inventories.count(loc.name) == 0)
2461 return m_detached_inventories[loc.name];
2469 void Client::inventoryAction(InventoryAction *a)
2472 Send it to the server
2474 sendInventoryAction(a);
2477 Predict some local inventory changes
2479 a->clientApply(this, this);
2485 ClientActiveObject * Client::getSelectedActiveObject(
2487 v3f from_pos_f_on_map,
2488 core::line3d<f32> shootline_on_map
2491 std::vector<DistanceSortedActiveObject> objects;
2493 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2495 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2498 // After this, the closest object is the first in the array.
2499 std::sort(objects.begin(), objects.end());
2501 for(u32 i=0; i<objects.size(); i++)
2503 ClientActiveObject *obj = objects[i].obj;
2505 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2506 if(selection_box == NULL)
2509 v3f pos = obj->getPosition();
2511 core::aabbox3d<f32> offsetted_box(
2512 selection_box->MinEdge + pos,
2513 selection_box->MaxEdge + pos
2516 if(offsetted_box.intersectsWithLine(shootline_on_map))
2518 //infostream<<"Returning selected object"<<std::endl;
2523 //infostream<<"No object selected; returning NULL."<<std::endl;
2527 void Client::printDebugInfo(std::ostream &os)
2529 //JMutexAutoLock lock1(m_fetchblock_mutex);
2530 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2532 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2533 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2534 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2538 std::list<std::string> Client::getConnectedPlayerNames()
2540 return m_env.getPlayerNames();
2543 float Client::getAnimationTime()
2545 return m_animation_time;
2548 int Client::getCrackLevel()
2550 return m_crack_level;
2553 void Client::setCrack(int level, v3s16 pos)
2555 int old_crack_level = m_crack_level;
2556 v3s16 old_crack_pos = m_crack_pos;
2558 m_crack_level = level;
2561 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2564 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2566 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2569 addUpdateMeshTaskForNode(pos, false, true);
2575 Player *player = m_env.getLocalPlayer();
2576 assert(player != NULL);
2580 u16 Client::getBreath()
2582 Player *player = m_env.getLocalPlayer();
2583 assert(player != NULL);
2584 return player->getBreath();
2587 bool Client::getChatMessage(std::wstring &message)
2589 if(m_chat_queue.size() == 0)
2591 message = m_chat_queue.pop_front();
2595 void Client::typeChatMessage(const std::wstring &message)
2597 // Discard empty line
2602 sendChatMessage(message);
2605 if (message[0] == L'/')
2607 m_chat_queue.push_back(
2608 (std::wstring)L"issued command: "+message);
2612 LocalPlayer *player = m_env.getLocalPlayer();
2613 assert(player != NULL);
2614 std::wstring name = narrow_to_wide(player->getName());
2615 m_chat_queue.push_back(
2616 (std::wstring)L"<"+name+L"> "+message);
2620 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2622 /*infostream<<"Client::addUpdateMeshTask(): "
2623 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2624 <<" ack_to_server="<<ack_to_server
2625 <<" urgent="<<urgent
2628 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2633 Create a task to update the mesh of the block
2636 MeshMakeData *data = new MeshMakeData(this);
2639 //TimeTaker timer("data fill");
2641 // Debug: 1-6ms, avg=2ms
2643 data->setCrack(m_crack_level, m_crack_pos);
2644 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2648 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2650 // Add task to queue
2651 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2653 /*infostream<<"Mesh update input queue size is "
2654 <<m_mesh_update_thread.m_queue_in.size()
2658 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2662 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2663 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2668 v3s16 p = blockpos + v3s16(0,0,0);
2669 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2670 addUpdateMeshTask(p, ack_to_server, urgent);
2672 catch(InvalidPositionException &e){}
2674 for (int i=0;i<6;i++)
2677 v3s16 p = blockpos + g_6dirs[i];
2678 addUpdateMeshTask(p, false, urgent);
2680 catch(InvalidPositionException &e){}
2684 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2688 infostream<<"Client::addUpdateMeshTaskForNode(): "
2689 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2693 v3s16 blockpos = getNodeBlockPos(nodepos);
2694 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2697 v3s16 p = blockpos + v3s16(0,0,0);
2698 addUpdateMeshTask(p, ack_to_server, urgent);
2700 catch(InvalidPositionException &e){}
2702 if(nodepos.X == blockpos_relative.X){
2704 v3s16 p = blockpos + v3s16(-1,0,0);
2705 addUpdateMeshTask(p, false, urgent);
2707 catch(InvalidPositionException &e){}
2709 if(nodepos.Y == blockpos_relative.Y){
2711 v3s16 p = blockpos + v3s16(0,-1,0);
2712 addUpdateMeshTask(p, false, urgent);
2714 catch(InvalidPositionException &e){}
2716 if(nodepos.Z == blockpos_relative.Z){
2718 v3s16 p = blockpos + v3s16(0,0,-1);
2719 addUpdateMeshTask(p, false, urgent);
2721 catch(InvalidPositionException &e){}
2725 ClientEvent Client::getClientEvent()
2727 if(m_client_event_queue.size() == 0)
2730 event.type = CE_NONE;
2733 return m_client_event_queue.pop_front();
2736 float Client::mediaReceiveProgress()
2738 if (m_media_downloader)
2739 return m_media_downloader->getProgress();
2741 return 1.0; // downloader only exists when not yet done
2744 void draw_load_screen(const std::wstring &text,
2745 IrrlichtDevice* device, gui::IGUIFont* font,
2746 float dtime=0 ,int percent=0, bool clouds=true);
2747 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2749 infostream<<"Client::afterContentReceived() started"<<std::endl;
2750 assert(m_itemdef_received);
2751 assert(m_nodedef_received);
2752 assert(mediaReceived());
2754 // Rebuild inherited images and recreate textures
2755 infostream<<"- Rebuilding images and textures"<<std::endl;
2756 m_tsrc->rebuildImagesAndTextures();
2759 infostream<<"- Rebuilding shaders"<<std::endl;
2760 m_shsrc->rebuildShaders();
2762 // Update node aliases
2763 infostream<<"- Updating node aliases"<<std::endl;
2764 m_nodedef->updateAliases(m_itemdef);
2766 // Update node textures
2767 infostream<<"- Updating node textures"<<std::endl;
2768 m_nodedef->updateTextures(m_tsrc);
2770 // Preload item textures and meshes if configured to
2771 if(g_settings->getBool("preload_item_visuals"))
2773 verbosestream<<"Updating item textures and meshes"<<std::endl;
2774 wchar_t* text = wgettext("Item textures...");
2775 draw_load_screen(text,device,font,0,0);
2776 std::set<std::string> names = m_itemdef->getAll();
2777 size_t size = names.size();
2780 for(std::set<std::string>::const_iterator
2781 i = names.begin(); i != names.end(); ++i){
2782 // Asking for these caches the result
2783 m_itemdef->getInventoryTexture(*i, this);
2784 m_itemdef->getWieldMesh(*i, this);
2786 percent = count*100/size;
2787 if (count%50 == 0) // only update every 50 item
2788 draw_load_screen(text,device,font,0,percent);
2793 // Start mesh update thread after setting up content definitions
2794 infostream<<"- Starting mesh update thread"<<std::endl;
2795 m_mesh_update_thread.Start();
2797 infostream<<"Client::afterContentReceived() done"<<std::endl;
2800 float Client::getRTT(void)
2803 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2804 } catch(con::PeerNotFoundException &e){
2809 // IGameDef interface
2811 IItemDefManager* Client::getItemDefManager()
2815 INodeDefManager* Client::getNodeDefManager()
2819 ICraftDefManager* Client::getCraftDefManager()
2822 //return m_craftdef;
2824 ITextureSource* Client::getTextureSource()
2828 IShaderSource* Client::getShaderSource()
2832 u16 Client::allocateUnknownNodeId(const std::string &name)
2834 errorstream<<"Client::allocateUnknownNodeId(): "
2835 <<"Client cannot allocate node IDs"<<std::endl;
2837 return CONTENT_IGNORE;
2839 ISoundManager* Client::getSoundManager()
2843 MtEventManager* Client::getEventManager()