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.
22 #include "clientserver.h"
23 #include "jmutexautolock.h"
27 #include "mapsector.h"
28 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
38 #include <IFileSystem.h>
41 #include "clientmap.h"
42 #include "filecache.h"
44 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "util/serialize.h"
49 #include "util/directiontables.h"
52 #include <curl/curl.h>
55 static std::string getMediaCacheDir()
57 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 void * MediaFetchThread::Thread()
228 log_register_thread("MediaFetchThread");
230 DSTACK(__FUNCTION_NAME);
232 BEGIN_DEBUG_EXCEPTION_HANDLER
237 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
238 i != m_file_requests.end(); ++i) {
239 curl = curl_easy_init();
241 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
242 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
243 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
244 std::ostringstream stream;
245 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
246 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
247 res = curl_easy_perform(curl);
248 if (res == CURLE_OK) {
249 std::string data = stream.str();
250 m_file_data.push_back(make_pair(i->name, data));
252 m_failed.push_back(*i);
253 infostream << "cURL request failed for " << i->name << std::endl;
255 curl_easy_cleanup(curl);
259 END_DEBUG_EXCEPTION_HANDLER(errorstream)
265 IrrlichtDevice *device,
266 const char *playername,
267 std::string password,
268 MapDrawControl &control,
269 IWritableTextureSource *tsrc,
270 IWritableShaderSource *shsrc,
271 IWritableItemDefManager *itemdef,
272 IWritableNodeDefManager *nodedef,
273 ISoundManager *sound,
274 MtEventManager *event,
283 m_mesh_update_thread(this),
285 new ClientMap(this, this, control,
286 device->getSceneManager()->getRootSceneNode(),
287 device->getSceneManager(), 666),
288 device->getSceneManager(),
291 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
293 m_server_ser_ver(SER_FMT_VER_INVALID),
295 m_inventory_updated(false),
296 m_inventory_from_server(NULL),
297 m_inventory_from_server_age(0.0),
302 m_password(password),
303 m_access_denied(false),
304 m_media_cache(getMediaCacheDir()),
305 m_media_receive_started(false),
307 m_media_received_count(0),
308 m_itemdef_received(false),
309 m_nodedef_received(false),
310 m_time_of_day_set(false),
311 m_last_time_of_day_f(-1),
312 m_time_of_day_update_timer(0),
313 m_recommended_send_interval(0.1),
314 m_removed_sounds_check_timer(0)
316 m_packetcounter_timer = 0.0;
317 //m_delete_unused_sectors_timer = 0.0;
318 m_connection_reinit_timer = 0.0;
319 m_avg_rtt_timer = 0.0;
320 m_playerpos_send_timer = 0.0;
321 m_ignore_damage_timer = 0.0;
327 Player *player = new LocalPlayer(this);
329 player->updateName(playername);
331 m_env.addPlayer(player);
334 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
335 m_media_fetch_threads.push_back(new MediaFetchThread(this));
341 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
345 m_mesh_update_thread.setRun(false);
346 while(m_mesh_update_thread.IsRunning())
348 while(!m_mesh_update_thread.m_queue_out.empty()) {
349 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
354 delete m_inventory_from_server;
356 // Delete detached inventories
358 for(std::map<std::string, Inventory*>::iterator
359 i = m_detached_inventories.begin();
360 i != m_detached_inventories.end(); i++){
365 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
366 i != m_media_fetch_threads.end(); ++i)
369 // cleanup 3d model meshes on client shutdown
370 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
371 scene::IAnimatedMesh * mesh =
372 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
375 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
379 void Client::connect(Address address)
381 DSTACK(__FUNCTION_NAME);
382 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
383 m_con.SetTimeoutMs(0);
384 m_con.Connect(address);
387 bool Client::connectedAndInitialized()
389 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
391 if(m_con.Connected() == false)
394 if(m_server_ser_ver == SER_FMT_VER_INVALID)
400 void Client::step(float dtime)
402 DSTACK(__FUNCTION_NAME);
408 if(m_ignore_damage_timer > dtime)
409 m_ignore_damage_timer -= dtime;
411 m_ignore_damage_timer = 0.0;
413 m_animation_time += dtime;
414 if(m_animation_time > 60.0)
415 m_animation_time -= 60.0;
417 m_time_of_day_update_timer += dtime;
419 //infostream<<"Client steps "<<dtime<<std::endl;
422 //TimeTaker timer("ReceiveAll()", m_device);
428 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
430 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
431 m_con.RunTimeouts(dtime);
438 float &counter = m_packetcounter_timer;
444 infostream<<"Client packetcounter (20s):"<<std::endl;
445 m_packetcounter.print(infostream);
446 m_packetcounter.clear();
450 // Get connection status
451 bool connected = connectedAndInitialized();
456 Delete unused sectors
458 NOTE: This jams the game for a while because deleting sectors
462 float &counter = m_delete_unused_sectors_timer;
470 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
472 core::list<v3s16> deleted_blocks;
474 float delete_unused_sectors_timeout =
475 g_settings->getFloat("client_delete_unused_sectors_timeout");
477 // Delete sector blocks
478 /*u32 num = m_env.getMap().unloadUnusedData
479 (delete_unused_sectors_timeout,
480 true, &deleted_blocks);*/
482 // Delete whole sectors
483 m_env.getMap().unloadUnusedData
484 (delete_unused_sectors_timeout,
487 if(deleted_blocks.size() > 0)
489 /*infostream<<"Client: Deleted blocks of "<<num
490 <<" unused sectors"<<std::endl;*/
491 /*infostream<<"Client: Deleted "<<num
492 <<" unused sectors"<<std::endl;*/
498 // Env is locked so con can be locked.
499 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
501 core::list<v3s16>::Iterator i = deleted_blocks.begin();
502 core::list<v3s16> sendlist;
505 if(sendlist.size() == 255 || i == deleted_blocks.end())
507 if(sendlist.size() == 0)
516 u32 replysize = 2+1+6*sendlist.size();
517 SharedBuffer<u8> reply(replysize);
518 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
519 reply[2] = sendlist.size();
521 for(core::list<v3s16>::Iterator
522 j = sendlist.begin();
523 j != sendlist.end(); j++)
525 writeV3S16(&reply[2+1+6*k], *j);
528 m_con.Send(PEER_ID_SERVER, 1, reply, true);
530 if(i == deleted_blocks.end())
536 sendlist.push_back(*i);
544 if(connected == false)
546 float &counter = m_connection_reinit_timer;
552 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
554 Player *myplayer = m_env.getLocalPlayer();
555 assert(myplayer != NULL);
557 // Send TOSERVER_INIT
558 // [0] u16 TOSERVER_INIT
559 // [2] u8 SER_FMT_VER_HIGHEST
560 // [3] u8[20] player_name
561 // [23] u8[28] password (new in some version)
562 // [51] u16 minimum supported network protocol version (added sometime)
563 // [53] u16 maximum supported network protocol version (added later than the previous one)
564 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
565 writeU16(&data[0], TOSERVER_INIT);
566 writeU8(&data[2], SER_FMT_VER_HIGHEST);
568 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
569 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
571 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
574 memset((char*)&data[23], 0, PASSWORD_SIZE);
575 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
577 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
578 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
580 // Send as unreliable
581 Send(0, data, false);
584 // Not connected, return
589 Do stuff if connected
593 Run Map's timers and unload unused data
595 const float map_timer_and_unload_dtime = 5.25;
596 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
598 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
599 std::list<v3s16> deleted_blocks;
600 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
601 g_settings->getFloat("client_unload_unused_data_timeout"),
604 /*if(deleted_blocks.size() > 0)
605 infostream<<"Client: Unloaded "<<deleted_blocks.size()
606 <<" unused blocks"<<std::endl;*/
610 NOTE: This loop is intentionally iterated the way it is.
613 std::list<v3s16>::iterator i = deleted_blocks.begin();
614 std::list<v3s16> sendlist;
617 if(sendlist.size() == 255 || i == deleted_blocks.end())
619 if(sendlist.size() == 0)
628 u32 replysize = 2+1+6*sendlist.size();
629 SharedBuffer<u8> reply(replysize);
630 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
631 reply[2] = sendlist.size();
633 for(std::list<v3s16>::iterator
634 j = sendlist.begin();
635 j != sendlist.end(); ++j)
637 writeV3S16(&reply[2+1+6*k], *j);
640 m_con.Send(PEER_ID_SERVER, 1, reply, true);
642 if(i == deleted_blocks.end())
648 sendlist.push_back(*i);
658 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
660 // Control local player (0ms)
661 LocalPlayer *player = m_env.getLocalPlayer();
662 assert(player != NULL);
663 player->applyControl(dtime);
665 //TimeTaker envtimer("env step", m_device);
674 ClientEnvEvent event = m_env.getClientEvent();
675 if(event.type == CEE_NONE)
679 else if(event.type == CEE_PLAYER_DAMAGE)
681 if(m_ignore_damage_timer <= 0)
683 u8 damage = event.player_damage.amount;
685 if(event.player_damage.send_to_server)
688 // Add to ClientEvent queue
690 event.type = CE_PLAYER_DAMAGE;
691 event.player_damage.amount = damage;
692 m_client_event_queue.push_back(event);
702 float &counter = m_avg_rtt_timer;
707 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
708 // connectedAndInitialized() is true, peer exists.
709 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
710 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
715 Send player position to server
718 float &counter = m_playerpos_send_timer;
720 if(counter >= m_recommended_send_interval)
728 Replace updated meshes
731 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
733 //TimeTaker timer("** Processing mesh update result queue");
736 /*infostream<<"Mesh update result queue size is "
737 <<m_mesh_update_thread.m_queue_out.size()
740 int num_processed_meshes = 0;
741 while(!m_mesh_update_thread.m_queue_out.empty())
743 num_processed_meshes++;
744 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
745 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
748 //JMutexAutoLock lock(block->mesh_mutex);
750 // Delete the old mesh
751 if(block->mesh != NULL)
753 // TODO: Remove hardware buffers of meshbuffers of block->mesh
758 // Replace with the new mesh
759 block->mesh = r.mesh;
763 if(r.ack_block_to_server)
765 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
766 <<","<<r.p.Z<<")"<<std::endl;*/
777 u32 replysize = 2+1+6;
778 SharedBuffer<u8> reply(replysize);
779 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
781 writeV3S16(&reply[3], r.p);
783 m_con.Send(PEER_ID_SERVER, 1, reply, true);
786 if(num_processed_meshes > 0)
787 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
793 if (m_media_receive_started) {
794 bool all_stopped = true;
795 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
796 thread != m_media_fetch_threads.end(); ++thread) {
797 all_stopped &= !(*thread)->IsRunning();
798 while (!(*thread)->m_file_data.empty()) {
799 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
800 ++m_media_received_count;
802 bool success = loadMedia(out.second, out.first);
804 verbosestream<<"Client: Loaded received media: "
805 <<"\""<<out.first<<"\". Caching."<<std::endl;
807 infostream<<"Client: Failed to load received media: "
808 <<"\""<<out.first<<"\". Not caching."<<std::endl;
812 bool did = fs::CreateAllDirs(getMediaCacheDir());
814 errorstream<<"Could not create media cache directory"
819 std::map<std::string, std::string>::iterator n;
820 n = m_media_name_sha1_map.find(out.first);
821 if(n == m_media_name_sha1_map.end())
822 errorstream<<"The server sent a file that has not "
823 <<"been announced."<<std::endl;
825 m_media_cache.update_sha1(out.second);
830 std::list<MediaRequest> fetch_failed;
831 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
832 thread != m_media_fetch_threads.end(); ++thread) {
833 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
834 request != (*thread)->m_failed.end(); ++request)
835 fetch_failed.push_back(*request);
836 (*thread)->m_failed.clear();
838 if (fetch_failed.size() > 0) {
839 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
840 << "Requesting them the usual way." << std::endl;
841 request_media(fetch_failed);
847 If the server didn't update the inventory in a while, revert
848 the local inventory (so the player notices the lag problem
849 and knows something is wrong).
851 if(m_inventory_from_server)
853 float interval = 10.0;
854 float count_before = floor(m_inventory_from_server_age / interval);
856 m_inventory_from_server_age += dtime;
858 float count_after = floor(m_inventory_from_server_age / interval);
860 if(count_after != count_before)
862 // Do this every <interval> seconds after TOCLIENT_INVENTORY
863 // Reset the locally changed inventory to the authoritative inventory
864 Player *player = m_env.getLocalPlayer();
865 player->inventory = *m_inventory_from_server;
866 m_inventory_updated = true;
871 Update positions of sounds attached to objects
874 for(std::map<int, u16>::iterator
875 i = m_sounds_to_objects.begin();
876 i != m_sounds_to_objects.end(); i++)
878 int client_id = i->first;
879 u16 object_id = i->second;
880 ClientActiveObject *cao = m_env.getActiveObject(object_id);
883 v3f pos = cao->getPosition();
884 m_sound->updateSoundPosition(client_id, pos);
889 Handle removed remotely initiated sounds
891 m_removed_sounds_check_timer += dtime;
892 if(m_removed_sounds_check_timer >= 2.32)
894 m_removed_sounds_check_timer = 0;
895 // Find removed sounds and clear references to them
896 std::set<s32> removed_server_ids;
897 for(std::map<s32, int>::iterator
898 i = m_sounds_server_to_client.begin();
899 i != m_sounds_server_to_client.end();)
901 s32 server_id = i->first;
902 int client_id = i->second;
904 if(!m_sound->soundExists(client_id)){
905 m_sounds_server_to_client.erase(server_id);
906 m_sounds_client_to_server.erase(client_id);
907 m_sounds_to_objects.erase(client_id);
908 removed_server_ids.insert(server_id);
912 if(removed_server_ids.size() != 0)
914 std::ostringstream os(std::ios_base::binary);
915 writeU16(os, TOSERVER_REMOVED_SOUNDS);
916 writeU16(os, removed_server_ids.size());
917 for(std::set<s32>::iterator i = removed_server_ids.begin();
918 i != removed_server_ids.end(); i++)
920 std::string s = os.str();
921 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
928 bool Client::loadMedia(const std::string &data, const std::string &filename)
930 // Silly irrlicht's const-incorrectness
931 Buffer<char> data_rw(data.c_str(), data.size());
935 const char *image_ext[] = {
936 ".png", ".jpg", ".bmp", ".tga",
937 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
940 name = removeStringEnd(filename, image_ext);
943 verbosestream<<"Client: Attempting to load image "
944 <<"file \""<<filename<<"\""<<std::endl;
946 io::IFileSystem *irrfs = m_device->getFileSystem();
947 video::IVideoDriver *vdrv = m_device->getVideoDriver();
949 // Create an irrlicht memory file
950 io::IReadFile *rfile = irrfs->createMemoryReadFile(
951 *data_rw, data_rw.getSize(), "_tempreadfile");
954 video::IImage *img = vdrv->createImageFromFile(rfile);
956 errorstream<<"Client: Cannot create image from data of "
957 <<"file \""<<filename<<"\""<<std::endl;
962 m_tsrc->insertSourceImage(filename, img);
969 const char *sound_ext[] = {
970 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
971 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
974 name = removeStringEnd(filename, sound_ext);
977 verbosestream<<"Client: Attempting to load sound "
978 <<"file \""<<filename<<"\""<<std::endl;
979 m_sound->loadSoundData(name, data);
983 const char *model_ext[] = {
984 ".x", ".b3d", ".md2", ".obj",
987 name = removeStringEnd(filename, model_ext);
990 verbosestream<<"Client: Storing model into Irrlicht: "
991 <<"\""<<filename<<"\""<<std::endl;
992 scene::ISceneManager *smgr = m_device->getSceneManager();
994 //check if mesh was already cached
995 scene::IAnimatedMesh *mesh =
996 smgr->getMeshCache()->getMeshByName(filename.c_str());
999 errorstream << "Multiple models with name: " << filename.c_str() <<
1000 " found replacing previous model!" << std::endl;
1002 smgr->getMeshCache()->removeMesh(mesh);
1006 io::IFileSystem *irrfs = m_device->getFileSystem();
1007 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1008 *data_rw, data_rw.getSize(), filename.c_str());
1011 mesh = smgr->getMesh(rfile);
1012 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1017 errorstream<<"Client: Don't know how to load file \""
1018 <<filename<<"\""<<std::endl;
1022 // Virtual methods from con::PeerHandler
1023 void Client::peerAdded(con::Peer *peer)
1025 infostream<<"Client::peerAdded(): peer->id="
1026 <<peer->id<<std::endl;
1028 void Client::deletingPeer(con::Peer *peer, bool timeout)
1030 infostream<<"Client::deletingPeer(): "
1031 "Server Peer is getting deleted "
1032 <<"(timeout="<<timeout<<")"<<std::endl;
1037 u16 number of files requested
1043 void Client::request_media(const std::list<MediaRequest> &file_requests)
1045 std::ostringstream os(std::ios_base::binary);
1046 writeU16(os, TOSERVER_REQUEST_MEDIA);
1047 writeU16(os, file_requests.size());
1049 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1050 i != file_requests.end(); ++i) {
1051 os<<serializeString(i->name);
1055 std::string s = os.str();
1056 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1058 Send(0, data, true);
1059 infostream<<"Client: Sending media request list to server ("
1060 <<file_requests.size()<<" files)"<<std::endl;
1063 void Client::ReceiveAll()
1065 DSTACK(__FUNCTION_NAME);
1066 u32 start_ms = porting::getTimeMs();
1069 // Limit time even if there would be huge amounts of data to
1071 if(porting::getTimeMs() > start_ms + 100)
1076 g_profiler->graphAdd("client_received_packets", 1);
1078 catch(con::NoIncomingDataException &e)
1082 catch(con::InvalidIncomingDataException &e)
1084 infostream<<"Client::ReceiveAll(): "
1085 "InvalidIncomingDataException: what()="
1086 <<e.what()<<std::endl;
1091 void Client::Receive()
1093 DSTACK(__FUNCTION_NAME);
1094 SharedBuffer<u8> data;
1098 //TimeTaker t1("con mutex and receive", m_device);
1099 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1100 datasize = m_con.Receive(sender_peer_id, data);
1102 //TimeTaker t1("ProcessData", m_device);
1103 ProcessData(*data, datasize, sender_peer_id);
1107 sender_peer_id given to this shall be quaranteed to be a valid peer
1109 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1111 DSTACK(__FUNCTION_NAME);
1113 // Ignore packets that don't even fit a command
1116 m_packetcounter.add(60000);
1120 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1122 //infostream<<"Client: received command="<<command<<std::endl;
1123 m_packetcounter.add((u16)command);
1126 If this check is removed, be sure to change the queue
1127 system to know the ids
1129 if(sender_peer_id != PEER_ID_SERVER)
1131 infostream<<"Client::ProcessData(): Discarding data not "
1132 "coming from server: peer_id="<<sender_peer_id
1137 u8 ser_version = m_server_ser_ver;
1139 //infostream<<"Client received command="<<(int)command<<std::endl;
1141 if(command == TOCLIENT_INIT)
1146 u8 deployed = data[2];
1148 infostream<<"Client: TOCLIENT_INIT received with "
1149 "deployed="<<((int)deployed&0xff)<<std::endl;
1151 if(deployed < SER_FMT_VER_LOWEST
1152 || deployed > SER_FMT_VER_HIGHEST)
1154 infostream<<"Client: TOCLIENT_INIT: Server sent "
1155 <<"unsupported ser_fmt_ver"<<std::endl;
1159 m_server_ser_ver = deployed;
1161 // Get player position
1162 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1163 if(datasize >= 2+1+6)
1164 playerpos_s16 = readV3S16(&data[2+1]);
1165 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1168 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1170 // Set player position
1171 Player *player = m_env.getLocalPlayer();
1172 assert(player != NULL);
1173 player->setPosition(playerpos_f);
1176 if(datasize >= 2+1+6+8)
1179 m_map_seed = readU64(&data[2+1+6]);
1180 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1183 if(datasize >= 2+1+6+8+4)
1186 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1187 infostream<<"Client: received recommended send interval "
1188 <<m_recommended_send_interval<<std::endl;
1193 SharedBuffer<u8> reply(replysize);
1194 writeU16(&reply[0], TOSERVER_INIT2);
1196 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1201 if(command == TOCLIENT_ACCESS_DENIED)
1203 // The server didn't like our password. Note, this needs
1204 // to be processed even if the serialisation format has
1205 // not been agreed yet, the same as TOCLIENT_INIT.
1206 m_access_denied = true;
1207 m_access_denied_reason = L"Unknown";
1210 std::string datastring((char*)&data[2], datasize-2);
1211 std::istringstream is(datastring, std::ios_base::binary);
1212 m_access_denied_reason = deSerializeWideString(is);
1217 if(ser_version == SER_FMT_VER_INVALID)
1219 infostream<<"Client: Server serialization"
1220 " format invalid or not initialized."
1221 " Skipping incoming command="<<command<<std::endl;
1225 // Just here to avoid putting the two if's together when
1226 // making some copypasta
1229 if(command == TOCLIENT_REMOVENODE)
1234 p.X = readS16(&data[2]);
1235 p.Y = readS16(&data[4]);
1236 p.Z = readS16(&data[6]);
1238 //TimeTaker t1("TOCLIENT_REMOVENODE");
1242 else if(command == TOCLIENT_ADDNODE)
1244 if(datasize < 8 + MapNode::serializedLength(ser_version))
1248 p.X = readS16(&data[2]);
1249 p.Y = readS16(&data[4]);
1250 p.Z = readS16(&data[6]);
1252 //TimeTaker t1("TOCLIENT_ADDNODE");
1255 n.deSerialize(&data[8], ser_version);
1259 else if(command == TOCLIENT_BLOCKDATA)
1261 // Ignore too small packet
1266 p.X = readS16(&data[2]);
1267 p.Y = readS16(&data[4]);
1268 p.Z = readS16(&data[6]);
1270 /*infostream<<"Client: Thread: BLOCKDATA for ("
1271 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1272 /*infostream<<"Client: Thread: BLOCKDATA for ("
1273 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1275 std::string datastring((char*)&data[8], datasize-8);
1276 std::istringstream istr(datastring, std::ios_base::binary);
1281 v2s16 p2d(p.X, p.Z);
1282 sector = m_env.getMap().emergeSector(p2d);
1284 assert(sector->getPos() == p2d);
1286 //TimeTaker timer("MapBlock deSerialize");
1289 block = sector->getBlockNoCreateNoEx(p.Y);
1293 Update an existing block
1295 //infostream<<"Updating"<<std::endl;
1296 block->deSerialize(istr, ser_version, false);
1303 //infostream<<"Creating new"<<std::endl;
1304 block = new MapBlock(&m_env.getMap(), p, this);
1305 block->deSerialize(istr, ser_version, false);
1306 sector->insertBlock(block);
1320 u32 replysize = 2+1+6;
1321 SharedBuffer<u8> reply(replysize);
1322 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1324 writeV3S16(&reply[3], p);
1326 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1330 Add it to mesh update queue and set it to be acknowledged after update.
1332 //infostream<<"Adding mesh update task for received block"<<std::endl;
1333 addUpdateMeshTaskWithEdge(p, true);
1335 else if(command == TOCLIENT_INVENTORY)
1340 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1343 //TimeTaker t2("mutex locking", m_device);
1344 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1347 //TimeTaker t3("istringstream init", m_device);
1348 std::string datastring((char*)&data[2], datasize-2);
1349 std::istringstream is(datastring, std::ios_base::binary);
1352 //m_env.printPlayers(infostream);
1354 //TimeTaker t4("player get", m_device);
1355 Player *player = m_env.getLocalPlayer();
1356 assert(player != NULL);
1359 //TimeTaker t1("inventory.deSerialize()", m_device);
1360 player->inventory.deSerialize(is);
1363 m_inventory_updated = true;
1365 delete m_inventory_from_server;
1366 m_inventory_from_server = new Inventory(player->inventory);
1367 m_inventory_from_server_age = 0.0;
1369 //infostream<<"Client got player inventory:"<<std::endl;
1370 //player->inventory.print(infostream);
1373 else if(command == TOCLIENT_TIME_OF_DAY)
1378 u16 time_of_day = readU16(&data[2]);
1379 time_of_day = time_of_day % 24000;
1380 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1381 float time_speed = 0;
1382 if(datasize >= 2 + 2 + 4){
1383 time_speed = readF1000(&data[4]);
1385 // Old message; try to approximate speed of time by ourselves
1386 float time_of_day_f = (float)time_of_day / 24000.0;
1387 float tod_diff_f = 0;
1388 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1389 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1391 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1392 m_last_time_of_day_f = time_of_day_f;
1393 float time_diff = m_time_of_day_update_timer;
1394 m_time_of_day_update_timer = 0;
1395 if(m_time_of_day_set){
1396 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1397 infostream<<"Client: Measured time_of_day speed (old format): "
1398 <<time_speed<<" tod_diff_f="<<tod_diff_f
1399 <<" time_diff="<<time_diff<<std::endl;
1403 // Update environment
1404 m_env.setTimeOfDay(time_of_day);
1405 m_env.setTimeOfDaySpeed(time_speed);
1406 m_time_of_day_set = true;
1408 u32 dr = m_env.getDayNightRatio();
1409 verbosestream<<"Client: time_of_day="<<time_of_day
1410 <<" time_speed="<<time_speed
1411 <<" dr="<<dr<<std::endl;
1413 else if(command == TOCLIENT_CHAT_MESSAGE)
1421 std::string datastring((char*)&data[2], datasize-2);
1422 std::istringstream is(datastring, std::ios_base::binary);
1425 is.read((char*)buf, 2);
1426 u16 len = readU16(buf);
1428 std::wstring message;
1429 for(u16 i=0; i<len; i++)
1431 is.read((char*)buf, 2);
1432 message += (wchar_t)readU16(buf);
1435 /*infostream<<"Client received chat message: "
1436 <<wide_to_narrow(message)<<std::endl;*/
1438 m_chat_queue.push_back(message);
1440 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1442 //if(g_settings->getBool("enable_experimental"))
1446 u16 count of removed objects
1447 for all removed objects {
1450 u16 count of added objects
1451 for all added objects {
1454 u32 initialization data length
1455 string initialization data
1460 // Get all data except the command number
1461 std::string datastring((char*)&data[2], datasize-2);
1462 // Throw them in an istringstream
1463 std::istringstream is(datastring, std::ios_base::binary);
1467 // Read removed objects
1469 u16 removed_count = readU16((u8*)buf);
1470 for(u16 i=0; i<removed_count; i++)
1473 u16 id = readU16((u8*)buf);
1476 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1477 m_env.removeActiveObject(id);
1481 // Read added objects
1483 u16 added_count = readU16((u8*)buf);
1484 for(u16 i=0; i<added_count; i++)
1487 u16 id = readU16((u8*)buf);
1489 u8 type = readU8((u8*)buf);
1490 std::string data = deSerializeLongString(is);
1493 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1494 m_env.addActiveObject(id, type, data);
1499 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1501 //if(g_settings->getBool("enable_experimental"))
1513 // Get all data except the command number
1514 std::string datastring((char*)&data[2], datasize-2);
1515 // Throw them in an istringstream
1516 std::istringstream is(datastring, std::ios_base::binary);
1518 while(is.eof() == false)
1522 u16 id = readU16((u8*)buf);
1526 u16 message_size = readU16((u8*)buf);
1527 std::string message;
1528 message.reserve(message_size);
1529 for(u16 i=0; i<message_size; i++)
1532 message.append(buf, 1);
1534 // Pass on to the environment
1536 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1537 m_env.processActiveObjectMessage(id, message);
1542 else if(command == TOCLIENT_MOVEMENT)
1544 std::string datastring((char*)&data[2], datasize-2);
1545 std::istringstream is(datastring, std::ios_base::binary);
1546 Player *player = m_env.getLocalPlayer();
1547 assert(player != NULL);
1549 player->movement_acceleration_default = readF1000(is) * BS;
1550 player->movement_acceleration_air = readF1000(is) * BS;
1551 player->movement_acceleration_fast = readF1000(is) * BS;
1552 player->movement_speed_walk = readF1000(is) * BS;
1553 player->movement_speed_crouch = readF1000(is) * BS;
1554 player->movement_speed_fast = readF1000(is) * BS;
1555 player->movement_speed_climb = readF1000(is) * BS;
1556 player->movement_speed_jump = readF1000(is) * BS;
1557 player->movement_liquid_fluidity = readF1000(is) * BS;
1558 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1559 player->movement_liquid_sink = readF1000(is) * BS;
1560 player->movement_gravity = readF1000(is) * BS;
1562 else if(command == TOCLIENT_HP)
1564 std::string datastring((char*)&data[2], datasize-2);
1565 std::istringstream is(datastring, std::ios_base::binary);
1566 Player *player = m_env.getLocalPlayer();
1567 assert(player != NULL);
1568 u8 oldhp = player->hp;
1574 // Add to ClientEvent queue
1576 event.type = CE_PLAYER_DAMAGE;
1577 event.player_damage.amount = oldhp - hp;
1578 m_client_event_queue.push_back(event);
1581 else if(command == TOCLIENT_MOVE_PLAYER)
1583 std::string datastring((char*)&data[2], datasize-2);
1584 std::istringstream is(datastring, std::ios_base::binary);
1585 Player *player = m_env.getLocalPlayer();
1586 assert(player != NULL);
1587 v3f pos = readV3F1000(is);
1588 f32 pitch = readF1000(is);
1589 f32 yaw = readF1000(is);
1590 player->setPosition(pos);
1591 /*player->setPitch(pitch);
1592 player->setYaw(yaw);*/
1594 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1595 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1601 Add to ClientEvent queue.
1602 This has to be sent to the main program because otherwise
1603 it would just force the pitch and yaw values to whatever
1604 the camera points to.
1607 event.type = CE_PLAYER_FORCE_MOVE;
1608 event.player_force_move.pitch = pitch;
1609 event.player_force_move.yaw = yaw;
1610 m_client_event_queue.push_back(event);
1612 // Ignore damage for a few seconds, so that the player doesn't
1613 // get damage from falling on ground
1614 m_ignore_damage_timer = 3.0;
1616 else if(command == TOCLIENT_PLAYERITEM)
1618 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1620 else if(command == TOCLIENT_DEATHSCREEN)
1622 std::string datastring((char*)&data[2], datasize-2);
1623 std::istringstream is(datastring, std::ios_base::binary);
1625 bool set_camera_point_target = readU8(is);
1626 v3f camera_point_target = readV3F1000(is);
1629 event.type = CE_DEATHSCREEN;
1630 event.deathscreen.set_camera_point_target = set_camera_point_target;
1631 event.deathscreen.camera_point_target_x = camera_point_target.X;
1632 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1633 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1634 m_client_event_queue.push_back(event);
1636 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1638 std::string datastring((char*)&data[2], datasize-2);
1639 std::istringstream is(datastring, std::ios_base::binary);
1641 // Mesh update thread must be stopped while
1642 // updating content definitions
1643 assert(!m_mesh_update_thread.IsRunning());
1645 int num_files = readU16(is);
1647 infostream<<"Client: Received media announcement: packet size: "
1648 <<datasize<<std::endl;
1650 std::list<MediaRequest> file_requests;
1652 for(int i=0; i<num_files; i++)
1654 //read file from cache
1655 std::string name = deSerializeString(is);
1656 std::string sha1_base64 = deSerializeString(is);
1658 // if name contains illegal characters, ignore the file
1659 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1660 errorstream<<"Client: ignoring illegal file name "
1661 <<"sent by server: \""<<name<<"\""<<std::endl;
1665 std::string sha1_raw = base64_decode(sha1_base64);
1666 std::string sha1_hex = hex_encode(sha1_raw);
1667 std::ostringstream tmp_os(std::ios_base::binary);
1668 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1669 m_media_name_sha1_map[name] = sha1_raw;
1671 // If found in cache, try to load it from there
1674 bool success = loadMedia(tmp_os.str(), name);
1676 verbosestream<<"Client: Loaded cached media: "
1677 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1680 infostream<<"Client: Failed to load cached media: "
1681 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1684 // Didn't load from cache; queue it to be requested
1685 verbosestream<<"Client: Adding file to request list: \""
1686 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1687 file_requests.push_back(MediaRequest(name));
1690 std::string remote_media = "";
1692 remote_media = deSerializeString(is);
1694 catch(SerializationError) {
1695 // not supported by server or turned off
1698 m_media_count = file_requests.size();
1699 m_media_receive_started = true;
1701 if (remote_media == "" || !USE_CURL) {
1702 request_media(file_requests);
1705 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1706 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1707 i != file_requests.end(); ++i) {
1708 (*cur)->m_file_requests.push_back(*i);
1710 if (cur == m_media_fetch_threads.end())
1711 cur = m_media_fetch_threads.begin();
1713 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1714 i != m_media_fetch_threads.end(); ++i) {
1715 (*i)->m_remote_url = remote_media;
1720 // notify server we received everything
1721 std::ostringstream os(std::ios_base::binary);
1722 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1723 std::string s = os.str();
1724 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1726 Send(0, data, true);
1729 event.type = CE_TEXTURES_UPDATED;
1730 m_client_event_queue.push_back(event);
1732 else if(command == TOCLIENT_MEDIA)
1734 if (m_media_count == 0)
1736 std::string datastring((char*)&data[2], datasize-2);
1737 std::istringstream is(datastring, std::ios_base::binary);
1739 // Mesh update thread must be stopped while
1740 // updating content definitions
1741 assert(!m_mesh_update_thread.IsRunning());
1745 u16 total number of file bunches
1746 u16 index of this bunch
1747 u32 number of files in this bunch
1755 int num_bunches = readU16(is);
1756 int bunch_i = readU16(is);
1757 int num_files = readU32(is);
1758 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1759 <<num_bunches<<" files="<<num_files
1760 <<" size="<<datasize<<std::endl;
1761 for(int i=0; i<num_files; i++){
1762 m_media_received_count++;
1763 std::string name = deSerializeString(is);
1764 std::string data = deSerializeLongString(is);
1766 // if name contains illegal characters, ignore the file
1767 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1768 errorstream<<"Client: ignoring illegal file name "
1769 <<"sent by server: \""<<name<<"\""<<std::endl;
1773 bool success = loadMedia(data, name);
1775 verbosestream<<"Client: Loaded received media: "
1776 <<"\""<<name<<"\". Caching."<<std::endl;
1778 infostream<<"Client: Failed to load received media: "
1779 <<"\""<<name<<"\". Not caching."<<std::endl;
1783 bool did = fs::CreateAllDirs(getMediaCacheDir());
1785 errorstream<<"Could not create media cache directory"
1790 std::map<std::string, std::string>::iterator n;
1791 n = m_media_name_sha1_map.find(name);
1792 if(n == m_media_name_sha1_map.end())
1793 errorstream<<"The server sent a file that has not "
1794 <<"been announced."<<std::endl;
1796 m_media_cache.update_sha1(data);
1801 event.type = CE_TEXTURES_UPDATED;
1802 m_client_event_queue.push_back(event);
1804 else if(command == TOCLIENT_TOOLDEF)
1806 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1808 else if(command == TOCLIENT_NODEDEF)
1810 infostream<<"Client: Received node definitions: packet size: "
1811 <<datasize<<std::endl;
1813 // Mesh update thread must be stopped while
1814 // updating content definitions
1815 assert(!m_mesh_update_thread.IsRunning());
1817 // Decompress node definitions
1818 std::string datastring((char*)&data[2], datasize-2);
1819 std::istringstream is(datastring, std::ios_base::binary);
1820 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1821 std::ostringstream tmp_os;
1822 decompressZlib(tmp_is, tmp_os);
1824 // Deserialize node definitions
1825 std::istringstream tmp_is2(tmp_os.str());
1826 m_nodedef->deSerialize(tmp_is2);
1827 m_nodedef_received = true;
1829 else if(command == TOCLIENT_CRAFTITEMDEF)
1831 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1833 else if(command == TOCLIENT_ITEMDEF)
1835 infostream<<"Client: Received item definitions: packet size: "
1836 <<datasize<<std::endl;
1838 // Mesh update thread must be stopped while
1839 // updating content definitions
1840 assert(!m_mesh_update_thread.IsRunning());
1842 // Decompress item definitions
1843 std::string datastring((char*)&data[2], datasize-2);
1844 std::istringstream is(datastring, std::ios_base::binary);
1845 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1846 std::ostringstream tmp_os;
1847 decompressZlib(tmp_is, tmp_os);
1849 // Deserialize node definitions
1850 std::istringstream tmp_is2(tmp_os.str());
1851 m_itemdef->deSerialize(tmp_is2);
1852 m_itemdef_received = true;
1854 else if(command == TOCLIENT_PLAY_SOUND)
1856 std::string datastring((char*)&data[2], datasize-2);
1857 std::istringstream is(datastring, std::ios_base::binary);
1859 s32 server_id = readS32(is);
1860 std::string name = deSerializeString(is);
1861 float gain = readF1000(is);
1862 int type = readU8(is); // 0=local, 1=positional, 2=object
1863 v3f pos = readV3F1000(is);
1864 u16 object_id = readU16(is);
1865 bool loop = readU8(is);
1870 client_id = m_sound->playSound(name, loop, gain);
1872 case 1: // positional
1873 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1876 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1878 pos = cao->getPosition();
1879 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1880 // TODO: Set up sound to move with object
1885 if(client_id != -1){
1886 m_sounds_server_to_client[server_id] = client_id;
1887 m_sounds_client_to_server[client_id] = server_id;
1889 m_sounds_to_objects[client_id] = object_id;
1892 else if(command == TOCLIENT_STOP_SOUND)
1894 std::string datastring((char*)&data[2], datasize-2);
1895 std::istringstream is(datastring, std::ios_base::binary);
1897 s32 server_id = readS32(is);
1898 std::map<s32, int>::iterator i =
1899 m_sounds_server_to_client.find(server_id);
1900 if(i != m_sounds_server_to_client.end()){
1901 int client_id = i->second;
1902 m_sound->stopSound(client_id);
1905 else if(command == TOCLIENT_PRIVILEGES)
1907 std::string datastring((char*)&data[2], datasize-2);
1908 std::istringstream is(datastring, std::ios_base::binary);
1910 m_privileges.clear();
1911 infostream<<"Client: Privileges updated: ";
1912 u16 num_privileges = readU16(is);
1913 for(u16 i=0; i<num_privileges; i++){
1914 std::string priv = deSerializeString(is);
1915 m_privileges.insert(priv);
1916 infostream<<priv<<" ";
1918 infostream<<std::endl;
1920 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1922 std::string datastring((char*)&data[2], datasize-2);
1923 std::istringstream is(datastring, std::ios_base::binary);
1925 // Store formspec in LocalPlayer
1926 Player *player = m_env.getLocalPlayer();
1927 assert(player != NULL);
1928 player->inventory_formspec = deSerializeLongString(is);
1930 else if(command == TOCLIENT_DETACHED_INVENTORY)
1932 std::string datastring((char*)&data[2], datasize-2);
1933 std::istringstream is(datastring, std::ios_base::binary);
1935 std::string name = deSerializeString(is);
1937 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1939 Inventory *inv = NULL;
1940 if(m_detached_inventories.count(name) > 0)
1941 inv = m_detached_inventories[name];
1943 inv = new Inventory(m_itemdef);
1944 m_detached_inventories[name] = inv;
1946 inv->deSerialize(is);
1948 else if(command == TOCLIENT_SHOW_FORMSPEC)
1950 std::string datastring((char*)&data[2], datasize-2);
1951 std::istringstream is(datastring, std::ios_base::binary);
1953 std::string formspec = deSerializeLongString(is);
1954 std::string formname = deSerializeString(is);
1957 event.type = CE_SHOW_FORMSPEC;
1958 // pointer is required as event is a struct only!
1959 // adding a std:string to a struct isn't possible
1960 event.show_formspec.formspec = new std::string(formspec);
1961 event.show_formspec.formname = new std::string(formname);
1962 m_client_event_queue.push_back(event);
1964 else if(command == TOCLIENT_SPAWN_PARTICLE)
1966 std::string datastring((char*)&data[2], datasize-2);
1967 std::istringstream is(datastring, std::ios_base::binary);
1969 v3f pos = readV3F1000(is);
1970 v3f vel = readV3F1000(is);
1971 v3f acc = readV3F1000(is);
1972 float expirationtime = readF1000(is);
1973 float size = readF1000(is);
1974 bool collisiondetection = readU8(is);
1975 std::string texture = deSerializeLongString(is);
1978 event.type = CE_SPAWN_PARTICLE;
1979 event.spawn_particle.pos = new v3f (pos);
1980 event.spawn_particle.vel = new v3f (vel);
1981 event.spawn_particle.acc = new v3f (acc);
1983 event.spawn_particle.expirationtime = expirationtime;
1984 event.spawn_particle.size = size;
1985 event.spawn_particle.collisiondetection =
1987 event.spawn_particle.texture = new std::string(texture);
1989 m_client_event_queue.push_back(event);
1991 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1993 std::string datastring((char*)&data[2], datasize-2);
1994 std::istringstream is(datastring, std::ios_base::binary);
1996 u16 amount = readU16(is);
1997 float spawntime = readF1000(is);
1998 v3f minpos = readV3F1000(is);
1999 v3f maxpos = readV3F1000(is);
2000 v3f minvel = readV3F1000(is);
2001 v3f maxvel = readV3F1000(is);
2002 v3f minacc = readV3F1000(is);
2003 v3f maxacc = readV3F1000(is);
2004 float minexptime = readF1000(is);
2005 float maxexptime = readF1000(is);
2006 float minsize = readF1000(is);
2007 float maxsize = readF1000(is);
2008 bool collisiondetection = readU8(is);
2009 std::string texture = deSerializeLongString(is);
2010 u32 id = readU32(is);
2013 event.type = CE_ADD_PARTICLESPAWNER;
2014 event.add_particlespawner.amount = amount;
2015 event.add_particlespawner.spawntime = spawntime;
2017 event.add_particlespawner.minpos = new v3f (minpos);
2018 event.add_particlespawner.maxpos = new v3f (maxpos);
2019 event.add_particlespawner.minvel = new v3f (minvel);
2020 event.add_particlespawner.maxvel = new v3f (maxvel);
2021 event.add_particlespawner.minacc = new v3f (minacc);
2022 event.add_particlespawner.maxacc = new v3f (maxacc);
2024 event.add_particlespawner.minexptime = minexptime;
2025 event.add_particlespawner.maxexptime = maxexptime;
2026 event.add_particlespawner.minsize = minsize;
2027 event.add_particlespawner.maxsize = maxsize;
2028 event.add_particlespawner.collisiondetection = collisiondetection;
2029 event.add_particlespawner.texture = new std::string(texture);
2030 event.add_particlespawner.id = id;
2032 m_client_event_queue.push_back(event);
2034 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2036 std::string datastring((char*)&data[2], datasize-2);
2037 std::istringstream is(datastring, std::ios_base::binary);
2039 u32 id = readU16(is);
2042 event.type = CE_DELETE_PARTICLESPAWNER;
2043 event.delete_particlespawner.id = id;
2045 m_client_event_queue.push_back(event);
2047 else if(command == TOCLIENT_HUDADD)
2049 std::string datastring((char *)&data[2], datasize - 2);
2050 std::istringstream is(datastring, std::ios_base::binary);
2052 u32 id = readU32(is);
2053 u8 type = readU8(is);
2054 v2f pos = readV2F1000(is);
2055 std::string name = deSerializeString(is);
2056 v2f scale = readV2F1000(is);
2057 std::string text = deSerializeString(is);
2058 u32 number = readU32(is);
2059 u32 item = readU32(is);
2060 u32 dir = readU32(is);
2061 v2f align = readV2F1000(is);
2062 v2f offset = readV2F1000(is);
2065 event.type = CE_HUDADD;
2066 event.hudadd.id = id;
2067 event.hudadd.type = type;
2068 event.hudadd.pos = new v2f(pos);
2069 event.hudadd.name = new std::string(name);
2070 event.hudadd.scale = new v2f(scale);
2071 event.hudadd.text = new std::string(text);
2072 event.hudadd.number = number;
2073 event.hudadd.item = item;
2074 event.hudadd.dir = dir;
2075 event.hudadd.align = new v2f(align);
2076 event.hudadd.offset = new v2f(offset);
2077 m_client_event_queue.push_back(event);
2079 else if(command == TOCLIENT_HUDRM)
2081 std::string datastring((char *)&data[2], datasize - 2);
2082 std::istringstream is(datastring, std::ios_base::binary);
2084 u32 id = readU32(is);
2087 event.type = CE_HUDRM;
2088 event.hudrm.id = id;
2089 m_client_event_queue.push_back(event);
2091 else if(command == TOCLIENT_HUDCHANGE)
2097 std::string datastring((char *)&data[2], datasize - 2);
2098 std::istringstream is(datastring, std::ios_base::binary);
2100 u32 id = readU32(is);
2101 u8 stat = (HudElementStat)readU8(is);
2103 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2104 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2105 v2fdata = readV2F1000(is);
2106 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2107 sdata = deSerializeString(is);
2109 intdata = readU32(is);
2112 event.type = CE_HUDCHANGE;
2113 event.hudchange.id = id;
2114 event.hudchange.stat = (HudElementStat)stat;
2115 event.hudchange.v2fdata = new v2f(v2fdata);
2116 event.hudchange.sdata = new std::string(sdata);
2117 event.hudchange.data = intdata;
2118 m_client_event_queue.push_back(event);
2120 else if(command == TOCLIENT_HUD_SET_FLAGS)
2122 std::string datastring((char *)&data[2], datasize - 2);
2123 std::istringstream is(datastring, std::ios_base::binary);
2125 Player *player = m_env.getLocalPlayer();
2126 assert(player != NULL);
2128 u32 flags = readU32(is);
2129 u32 mask = readU32(is);
2131 player->hud_flags &= ~mask;
2132 player->hud_flags |= flags;
2134 else if(command == TOCLIENT_HUD_SET_PARAM)
2136 std::string datastring((char *)&data[2], datasize - 2);
2137 std::istringstream is(datastring, std::ios_base::binary);
2139 Player *player = m_env.getLocalPlayer();
2140 assert(player != NULL);
2142 u16 param = readU16(is);
2143 std::string value = deSerializeString(is);
2145 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2146 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2147 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2148 player->hud_hotbar_itemcount = hotbar_itemcount;
2153 infostream<<"Client: Ignoring unknown command "
2154 <<command<<std::endl;
2158 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2160 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2161 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2164 void Client::interact(u8 action, const PointedThing& pointed)
2166 if(connectedAndInitialized() == false){
2167 infostream<<"Client::interact() "
2168 "cancelled (not connected)"
2173 std::ostringstream os(std::ios_base::binary);
2179 [5] u32 length of the next item
2180 [9] serialized PointedThing
2182 0: start digging (from undersurface) or use
2183 1: stop digging (all parameters ignored)
2184 2: digging completed
2185 3: place block or item (to abovesurface)
2188 writeU16(os, TOSERVER_INTERACT);
2189 writeU8(os, action);
2190 writeU16(os, getPlayerItem());
2191 std::ostringstream tmp_os(std::ios::binary);
2192 pointed.serialize(tmp_os);
2193 os<<serializeLongString(tmp_os.str());
2195 std::string s = os.str();
2196 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2199 Send(0, data, true);
2202 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2203 const std::map<std::string, std::string> &fields)
2205 std::ostringstream os(std::ios_base::binary);
2207 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2209 os<<serializeString(formname);
2210 writeU16(os, fields.size());
2211 for(std::map<std::string, std::string>::const_iterator
2212 i = fields.begin(); i != fields.end(); i++){
2213 const std::string &name = i->first;
2214 const std::string &value = i->second;
2215 os<<serializeString(name);
2216 os<<serializeLongString(value);
2220 std::string s = os.str();
2221 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2223 Send(0, data, true);
2226 void Client::sendInventoryFields(const std::string &formname,
2227 const std::map<std::string, std::string> &fields)
2229 std::ostringstream os(std::ios_base::binary);
2231 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2232 os<<serializeString(formname);
2233 writeU16(os, fields.size());
2234 for(std::map<std::string, std::string>::const_iterator
2235 i = fields.begin(); i != fields.end(); i++){
2236 const std::string &name = i->first;
2237 const std::string &value = i->second;
2238 os<<serializeString(name);
2239 os<<serializeLongString(value);
2243 std::string s = os.str();
2244 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2246 Send(0, data, true);
2249 void Client::sendInventoryAction(InventoryAction *a)
2251 std::ostringstream os(std::ios_base::binary);
2255 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2256 os.write((char*)buf, 2);
2261 std::string s = os.str();
2262 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2264 Send(0, data, true);
2267 void Client::sendChatMessage(const std::wstring &message)
2269 std::ostringstream os(std::ios_base::binary);
2273 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2274 os.write((char*)buf, 2);
2277 writeU16(buf, message.size());
2278 os.write((char*)buf, 2);
2281 for(u32 i=0; i<message.size(); i++)
2285 os.write((char*)buf, 2);
2289 std::string s = os.str();
2290 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2292 Send(0, data, true);
2295 void Client::sendChangePassword(const std::wstring oldpassword,
2296 const std::wstring newpassword)
2298 Player *player = m_env.getLocalPlayer();
2302 std::string playername = player->getName();
2303 std::string oldpwd = translatePassword(playername, oldpassword);
2304 std::string newpwd = translatePassword(playername, newpassword);
2306 std::ostringstream os(std::ios_base::binary);
2307 u8 buf[2+PASSWORD_SIZE*2];
2309 [0] u16 TOSERVER_PASSWORD
2310 [2] u8[28] old password
2311 [30] u8[28] new password
2314 writeU16(buf, TOSERVER_PASSWORD);
2315 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2317 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2318 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2320 buf[2+PASSWORD_SIZE-1] = 0;
2321 buf[30+PASSWORD_SIZE-1] = 0;
2322 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2325 std::string s = os.str();
2326 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2328 Send(0, data, true);
2332 void Client::sendDamage(u8 damage)
2334 DSTACK(__FUNCTION_NAME);
2335 std::ostringstream os(std::ios_base::binary);
2337 writeU16(os, TOSERVER_DAMAGE);
2338 writeU8(os, damage);
2341 std::string s = os.str();
2342 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2344 Send(0, data, true);
2347 void Client::sendRespawn()
2349 DSTACK(__FUNCTION_NAME);
2350 std::ostringstream os(std::ios_base::binary);
2352 writeU16(os, TOSERVER_RESPAWN);
2355 std::string s = os.str();
2356 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2358 Send(0, data, true);
2361 void Client::sendPlayerPos()
2363 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2365 LocalPlayer *myplayer = m_env.getLocalPlayer();
2366 if(myplayer == NULL)
2369 // Save bandwidth by only updating position when something changed
2370 if(myplayer->last_position == myplayer->getPosition() &&
2371 myplayer->last_speed == myplayer->getSpeed() &&
2372 myplayer->last_pitch == myplayer->getPitch() &&
2373 myplayer->last_yaw == myplayer->getYaw() &&
2374 myplayer->last_keyPressed == myplayer->keyPressed)
2377 myplayer->last_position = myplayer->getPosition();
2378 myplayer->last_speed = myplayer->getSpeed();
2379 myplayer->last_pitch = myplayer->getPitch();
2380 myplayer->last_yaw = myplayer->getYaw();
2381 myplayer->last_keyPressed = myplayer->keyPressed;
2385 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2386 our_peer_id = m_con.GetPeerID();
2389 // Set peer id if not set already
2390 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2391 myplayer->peer_id = our_peer_id;
2392 // Check that an existing peer_id is the same as the connection's
2393 assert(myplayer->peer_id == our_peer_id);
2395 v3f pf = myplayer->getPosition();
2396 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2397 v3f sf = myplayer->getSpeed();
2398 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2399 s32 pitch = myplayer->getPitch() * 100;
2400 s32 yaw = myplayer->getYaw() * 100;
2401 u32 keyPressed=myplayer->keyPressed;
2405 [2] v3s32 position*100
2406 [2+12] v3s32 speed*100
2407 [2+12+12] s32 pitch*100
2408 [2+12+12+4] s32 yaw*100
2409 [2+12+12+4+4] u32 keyPressed
2411 SharedBuffer<u8> data(2+12+12+4+4+4);
2412 writeU16(&data[0], TOSERVER_PLAYERPOS);
2413 writeV3S32(&data[2], position);
2414 writeV3S32(&data[2+12], speed);
2415 writeS32(&data[2+12+12], pitch);
2416 writeS32(&data[2+12+12+4], yaw);
2417 writeU32(&data[2+12+12+4+4], keyPressed);
2418 // Send as unreliable
2419 Send(0, data, false);
2422 void Client::sendPlayerItem(u16 item)
2424 Player *myplayer = m_env.getLocalPlayer();
2425 if(myplayer == NULL)
2428 u16 our_peer_id = m_con.GetPeerID();
2430 // Set peer id if not set already
2431 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2432 myplayer->peer_id = our_peer_id;
2433 // Check that an existing peer_id is the same as the connection's
2434 assert(myplayer->peer_id == our_peer_id);
2436 SharedBuffer<u8> data(2+2);
2437 writeU16(&data[0], TOSERVER_PLAYERITEM);
2438 writeU16(&data[2], item);
2441 Send(0, data, true);
2444 void Client::removeNode(v3s16 p)
2446 std::map<v3s16, MapBlock*> modified_blocks;
2450 //TimeTaker t("removeNodeAndUpdate", m_device);
2451 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2453 catch(InvalidPositionException &e)
2457 // add urgent task to update the modified node
2458 addUpdateMeshTaskForNode(p, false, true);
2460 for(std::map<v3s16, MapBlock * >::iterator
2461 i = modified_blocks.begin();
2462 i != modified_blocks.end(); ++i)
2464 addUpdateMeshTaskWithEdge(i->first);
2468 void Client::addNode(v3s16 p, MapNode n)
2470 TimeTaker timer1("Client::addNode()");
2472 std::map<v3s16, MapBlock*> modified_blocks;
2476 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2477 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2479 catch(InvalidPositionException &e)
2482 for(std::map<v3s16, MapBlock * >::iterator
2483 i = modified_blocks.begin();
2484 i != modified_blocks.end(); ++i)
2486 addUpdateMeshTaskWithEdge(i->first);
2490 void Client::setPlayerControl(PlayerControl &control)
2492 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2493 LocalPlayer *player = m_env.getLocalPlayer();
2494 assert(player != NULL);
2495 player->control = control;
2498 void Client::selectPlayerItem(u16 item)
2500 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2501 m_playeritem = item;
2502 m_inventory_updated = true;
2503 sendPlayerItem(item);
2506 // Returns true if the inventory of the local player has been
2507 // updated from the server. If it is true, it is set to false.
2508 bool Client::getLocalInventoryUpdated()
2510 // m_inventory_updated is behind envlock
2511 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2512 bool updated = m_inventory_updated;
2513 m_inventory_updated = false;
2517 // Copies the inventory of the local player to parameter
2518 void Client::getLocalInventory(Inventory &dst)
2520 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2521 Player *player = m_env.getLocalPlayer();
2522 assert(player != NULL);
2523 dst = player->inventory;
2526 Inventory* Client::getInventory(const InventoryLocation &loc)
2529 case InventoryLocation::UNDEFINED:
2532 case InventoryLocation::CURRENT_PLAYER:
2534 Player *player = m_env.getLocalPlayer();
2535 assert(player != NULL);
2536 return &player->inventory;
2539 case InventoryLocation::PLAYER:
2541 Player *player = m_env.getPlayer(loc.name.c_str());
2544 return &player->inventory;
2547 case InventoryLocation::NODEMETA:
2549 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2552 return meta->getInventory();
2555 case InventoryLocation::DETACHED:
2557 if(m_detached_inventories.count(loc.name) == 0)
2559 return m_detached_inventories[loc.name];
2567 void Client::inventoryAction(InventoryAction *a)
2570 Send it to the server
2572 sendInventoryAction(a);
2575 Predict some local inventory changes
2577 a->clientApply(this, this);
2583 ClientActiveObject * Client::getSelectedActiveObject(
2585 v3f from_pos_f_on_map,
2586 core::line3d<f32> shootline_on_map
2589 std::vector<DistanceSortedActiveObject> objects;
2591 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2593 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2596 // After this, the closest object is the first in the array.
2597 std::sort(objects.begin(), objects.end());
2599 for(u32 i=0; i<objects.size(); i++)
2601 ClientActiveObject *obj = objects[i].obj;
2603 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2604 if(selection_box == NULL)
2607 v3f pos = obj->getPosition();
2609 core::aabbox3d<f32> offsetted_box(
2610 selection_box->MinEdge + pos,
2611 selection_box->MaxEdge + pos
2614 if(offsetted_box.intersectsWithLine(shootline_on_map))
2616 //infostream<<"Returning selected object"<<std::endl;
2621 //infostream<<"No object selected; returning NULL."<<std::endl;
2625 void Client::printDebugInfo(std::ostream &os)
2627 //JMutexAutoLock lock1(m_fetchblock_mutex);
2628 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2630 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2631 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2632 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2636 std::list<std::string> Client::getConnectedPlayerNames()
2638 return m_env.getPlayerNames();
2641 float Client::getAnimationTime()
2643 return m_animation_time;
2646 int Client::getCrackLevel()
2648 return m_crack_level;
2651 void Client::setCrack(int level, v3s16 pos)
2653 int old_crack_level = m_crack_level;
2654 v3s16 old_crack_pos = m_crack_pos;
2656 m_crack_level = level;
2659 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2662 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2664 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2667 addUpdateMeshTaskForNode(pos, false, true);
2673 Player *player = m_env.getLocalPlayer();
2674 assert(player != NULL);
2678 u16 Client::getBreath()
2680 Player *player = m_env.getLocalPlayer();
2681 assert(player != NULL);
2682 return player->breath;
2685 bool Client::getChatMessage(std::wstring &message)
2687 if(m_chat_queue.size() == 0)
2689 message = m_chat_queue.pop_front();
2693 void Client::typeChatMessage(const std::wstring &message)
2695 // Discard empty line
2700 sendChatMessage(message);
2703 if (message[0] == L'/')
2705 m_chat_queue.push_back(
2706 (std::wstring)L"issued command: "+message);
2710 LocalPlayer *player = m_env.getLocalPlayer();
2711 assert(player != NULL);
2712 std::wstring name = narrow_to_wide(player->getName());
2713 m_chat_queue.push_back(
2714 (std::wstring)L"<"+name+L"> "+message);
2718 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2720 /*infostream<<"Client::addUpdateMeshTask(): "
2721 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2722 <<" ack_to_server="<<ack_to_server
2723 <<" urgent="<<urgent
2726 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2731 Create a task to update the mesh of the block
2734 MeshMakeData *data = new MeshMakeData(this);
2737 //TimeTaker timer("data fill");
2739 // Debug: 1-6ms, avg=2ms
2741 data->setCrack(m_crack_level, m_crack_pos);
2742 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2746 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2748 // Add task to queue
2749 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2751 /*infostream<<"Mesh update input queue size is "
2752 <<m_mesh_update_thread.m_queue_in.size()
2756 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2760 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2761 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2766 v3s16 p = blockpos + v3s16(0,0,0);
2767 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2768 addUpdateMeshTask(p, ack_to_server, urgent);
2770 catch(InvalidPositionException &e){}
2772 for (int i=0;i<6;i++)
2775 v3s16 p = blockpos + g_6dirs[i];
2776 addUpdateMeshTask(p, false, urgent);
2778 catch(InvalidPositionException &e){}
2782 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2786 infostream<<"Client::addUpdateMeshTaskForNode(): "
2787 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2791 v3s16 blockpos = getNodeBlockPos(nodepos);
2792 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2795 v3s16 p = blockpos + v3s16(0,0,0);
2796 addUpdateMeshTask(p, ack_to_server, urgent);
2798 catch(InvalidPositionException &e){}
2800 if(nodepos.X == blockpos_relative.X){
2802 v3s16 p = blockpos + v3s16(-1,0,0);
2803 addUpdateMeshTask(p, false, urgent);
2805 catch(InvalidPositionException &e){}
2807 if(nodepos.Y == blockpos_relative.Y){
2809 v3s16 p = blockpos + v3s16(0,-1,0);
2810 addUpdateMeshTask(p, false, urgent);
2812 catch(InvalidPositionException &e){}
2814 if(nodepos.Z == blockpos_relative.Z){
2816 v3s16 p = blockpos + v3s16(0,0,-1);
2817 addUpdateMeshTask(p, false, urgent);
2819 catch(InvalidPositionException &e){}
2823 ClientEvent Client::getClientEvent()
2825 if(m_client_event_queue.size() == 0)
2828 event.type = CE_NONE;
2831 return m_client_event_queue.pop_front();
2834 void draw_load_screen(const std::wstring &text,
2835 IrrlichtDevice* device, gui::IGUIFont* font,
2836 float dtime=0 ,int percent=0, bool clouds=true);
2837 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2839 infostream<<"Client::afterContentReceived() started"<<std::endl;
2840 assert(m_itemdef_received);
2841 assert(m_nodedef_received);
2842 assert(texturesReceived());
2844 // remove the information about which checksum each texture
2846 m_media_name_sha1_map.clear();
2848 // Rebuild inherited images and recreate textures
2849 infostream<<"- Rebuilding images and textures"<<std::endl;
2850 m_tsrc->rebuildImagesAndTextures();
2853 m_shsrc->rebuildShaders();
2855 // Update node aliases
2856 infostream<<"- Updating node aliases"<<std::endl;
2857 m_nodedef->updateAliases(m_itemdef);
2859 // Update node textures
2860 infostream<<"- Updating node textures"<<std::endl;
2861 m_nodedef->updateTextures(m_tsrc);
2863 // Preload item textures and meshes if configured to
2864 if(g_settings->getBool("preload_item_visuals"))
2866 verbosestream<<"Updating item textures and meshes"<<std::endl;
2867 wchar_t* text = wgettext("Item textures...");
2868 draw_load_screen(text,device,font,0,0);
2869 std::set<std::string> names = m_itemdef->getAll();
2870 size_t size = names.size();
2873 for(std::set<std::string>::const_iterator
2874 i = names.begin(); i != names.end(); ++i){
2875 // Asking for these caches the result
2876 m_itemdef->getInventoryTexture(*i, this);
2877 m_itemdef->getWieldMesh(*i, this);
2879 percent = count*100/size;
2880 if (count%50 == 0) // only update every 50 item
2881 draw_load_screen(text,device,font,0,percent);
2886 // Start mesh update thread after setting up content definitions
2887 infostream<<"- Starting mesh update thread"<<std::endl;
2888 m_mesh_update_thread.Start();
2890 infostream<<"Client::afterContentReceived() done"<<std::endl;
2893 float Client::getRTT(void)
2896 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2897 } catch(con::PeerNotFoundException &e){
2902 // IGameDef interface
2904 IItemDefManager* Client::getItemDefManager()
2908 INodeDefManager* Client::getNodeDefManager()
2912 ICraftDefManager* Client::getCraftDefManager()
2915 //return m_craftdef;
2917 ITextureSource* Client::getTextureSource()
2921 IShaderSource* Client::getShaderSource()
2925 u16 Client::allocateUnknownNodeId(const std::string &name)
2927 errorstream<<"Client::allocateUnknownNodeId(): "
2928 <<"Client cannot allocate node IDs"<<std::endl;
2930 return CONTENT_IGNORE;
2932 ISoundManager* Client::getSoundManager()
2936 MtEventManager* Client::getEventManager()