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
282 m_mesh_update_thread(this),
284 new ClientMap(this, this, control,
285 device->getSceneManager()->getRootSceneNode(),
286 device->getSceneManager(), 666),
287 device->getSceneManager(),
290 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
292 m_server_ser_ver(SER_FMT_VER_INVALID),
294 m_inventory_updated(false),
295 m_inventory_from_server(NULL),
296 m_inventory_from_server_age(0.0),
301 m_password(password),
302 m_access_denied(false),
303 m_media_cache(getMediaCacheDir()),
304 m_media_receive_started(false),
306 m_media_received_count(0),
307 m_itemdef_received(false),
308 m_nodedef_received(false),
309 m_time_of_day_set(false),
310 m_last_time_of_day_f(-1),
311 m_time_of_day_update_timer(0),
312 m_recommended_send_interval(0.1),
313 m_removed_sounds_check_timer(0)
315 m_packetcounter_timer = 0.0;
316 //m_delete_unused_sectors_timer = 0.0;
317 m_connection_reinit_timer = 0.0;
318 m_avg_rtt_timer = 0.0;
319 m_playerpos_send_timer = 0.0;
320 m_ignore_damage_timer = 0.0;
322 // Build main texture atlas, now that the GameDef exists (that is, us)
323 if(g_settings->getBool("enable_texture_atlas"))
324 m_tsrc->buildMainAtlas(this);
326 infostream<<"Not building texture atlas."<<std::endl;
332 Player *player = new LocalPlayer(this);
334 player->updateName(playername);
336 m_env.addPlayer(player);
339 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
340 m_media_fetch_threads.push_back(new MediaFetchThread(this));
346 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
350 m_mesh_update_thread.setRun(false);
351 while(m_mesh_update_thread.IsRunning())
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;
761 if(r.ack_block_to_server)
763 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
764 <<","<<r.p.Z<<")"<<std::endl;*/
775 u32 replysize = 2+1+6;
776 SharedBuffer<u8> reply(replysize);
777 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
779 writeV3S16(&reply[3], r.p);
781 m_con.Send(PEER_ID_SERVER, 1, reply, true);
784 if(num_processed_meshes > 0)
785 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
791 if (m_media_receive_started) {
792 bool all_stopped = true;
793 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
794 thread != m_media_fetch_threads.end(); ++thread) {
795 all_stopped &= !(*thread)->IsRunning();
796 while (!(*thread)->m_file_data.empty()) {
797 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
798 ++m_media_received_count;
800 bool success = loadMedia(out.second, out.first);
802 verbosestream<<"Client: Loaded received media: "
803 <<"\""<<out.first<<"\". Caching."<<std::endl;
805 infostream<<"Client: Failed to load received media: "
806 <<"\""<<out.first<<"\". Not caching."<<std::endl;
810 bool did = fs::CreateAllDirs(getMediaCacheDir());
812 errorstream<<"Could not create media cache directory"
817 std::map<std::string, std::string>::iterator n;
818 n = m_media_name_sha1_map.find(out.first);
819 if(n == m_media_name_sha1_map.end())
820 errorstream<<"The server sent a file that has not "
821 <<"been announced."<<std::endl;
823 m_media_cache.update_sha1(out.second);
828 std::list<MediaRequest> fetch_failed;
829 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
830 thread != m_media_fetch_threads.end(); ++thread) {
831 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
832 request != (*thread)->m_failed.end(); ++request)
833 fetch_failed.push_back(*request);
834 (*thread)->m_failed.clear();
836 if (fetch_failed.size() > 0) {
837 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
838 << "Requesting them the usual way." << std::endl;
839 request_media(fetch_failed);
845 If the server didn't update the inventory in a while, revert
846 the local inventory (so the player notices the lag problem
847 and knows something is wrong).
849 if(m_inventory_from_server)
851 float interval = 10.0;
852 float count_before = floor(m_inventory_from_server_age / interval);
854 m_inventory_from_server_age += dtime;
856 float count_after = floor(m_inventory_from_server_age / interval);
858 if(count_after != count_before)
860 // Do this every <interval> seconds after TOCLIENT_INVENTORY
861 // Reset the locally changed inventory to the authoritative inventory
862 Player *player = m_env.getLocalPlayer();
863 player->inventory = *m_inventory_from_server;
864 m_inventory_updated = true;
869 Update positions of sounds attached to objects
872 for(std::map<int, u16>::iterator
873 i = m_sounds_to_objects.begin();
874 i != m_sounds_to_objects.end(); i++)
876 int client_id = i->first;
877 u16 object_id = i->second;
878 ClientActiveObject *cao = m_env.getActiveObject(object_id);
881 v3f pos = cao->getPosition();
882 m_sound->updateSoundPosition(client_id, pos);
887 Handle removed remotely initiated sounds
889 m_removed_sounds_check_timer += dtime;
890 if(m_removed_sounds_check_timer >= 2.32)
892 m_removed_sounds_check_timer = 0;
893 // Find removed sounds and clear references to them
894 std::set<s32> removed_server_ids;
895 for(std::map<s32, int>::iterator
896 i = m_sounds_server_to_client.begin();
897 i != m_sounds_server_to_client.end();)
899 s32 server_id = i->first;
900 int client_id = i->second;
902 if(!m_sound->soundExists(client_id)){
903 m_sounds_server_to_client.erase(server_id);
904 m_sounds_client_to_server.erase(client_id);
905 m_sounds_to_objects.erase(client_id);
906 removed_server_ids.insert(server_id);
910 if(removed_server_ids.size() != 0)
912 std::ostringstream os(std::ios_base::binary);
913 writeU16(os, TOSERVER_REMOVED_SOUNDS);
914 writeU16(os, removed_server_ids.size());
915 for(std::set<s32>::iterator i = removed_server_ids.begin();
916 i != removed_server_ids.end(); i++)
918 std::string s = os.str();
919 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
926 bool Client::loadMedia(const std::string &data, const std::string &filename)
928 // Silly irrlicht's const-incorrectness
929 Buffer<char> data_rw(data.c_str(), data.size());
933 const char *image_ext[] = {
934 ".png", ".jpg", ".bmp", ".tga",
935 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
938 name = removeStringEnd(filename, image_ext);
941 verbosestream<<"Client: Attempting to load image "
942 <<"file \""<<filename<<"\""<<std::endl;
944 io::IFileSystem *irrfs = m_device->getFileSystem();
945 video::IVideoDriver *vdrv = m_device->getVideoDriver();
947 // Create an irrlicht memory file
948 io::IReadFile *rfile = irrfs->createMemoryReadFile(
949 *data_rw, data_rw.getSize(), "_tempreadfile");
952 video::IImage *img = vdrv->createImageFromFile(rfile);
954 errorstream<<"Client: Cannot create image from data of "
955 <<"file \""<<filename<<"\""<<std::endl;
960 m_tsrc->insertSourceImage(filename, img);
967 const char *sound_ext[] = {
968 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
969 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
972 name = removeStringEnd(filename, sound_ext);
975 verbosestream<<"Client: Attempting to load sound "
976 <<"file \""<<filename<<"\""<<std::endl;
977 m_sound->loadSoundData(name, data);
981 const char *model_ext[] = {
982 ".x", ".b3d", ".md2", ".obj",
985 name = removeStringEnd(filename, model_ext);
988 verbosestream<<"Client: Storing model into Irrlicht: "
989 <<"\""<<filename<<"\""<<std::endl;
990 scene::ISceneManager *smgr = m_device->getSceneManager();
992 //check if mesh was already cached
993 scene::IAnimatedMesh *mesh =
994 smgr->getMeshCache()->getMeshByName(filename.c_str());
997 errorstream << "Multiple models with name: " << filename.c_str() <<
998 " found replacing previous model!" << std::endl;
1000 smgr->getMeshCache()->removeMesh(mesh);
1004 io::IFileSystem *irrfs = m_device->getFileSystem();
1005 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1006 *data_rw, data_rw.getSize(), filename.c_str());
1009 mesh = smgr->getMesh(rfile);
1010 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1015 errorstream<<"Client: Don't know how to load file \""
1016 <<filename<<"\""<<std::endl;
1020 // Virtual methods from con::PeerHandler
1021 void Client::peerAdded(con::Peer *peer)
1023 infostream<<"Client::peerAdded(): peer->id="
1024 <<peer->id<<std::endl;
1026 void Client::deletingPeer(con::Peer *peer, bool timeout)
1028 infostream<<"Client::deletingPeer(): "
1029 "Server Peer is getting deleted "
1030 <<"(timeout="<<timeout<<")"<<std::endl;
1035 u16 number of files requested
1041 void Client::request_media(const std::list<MediaRequest> &file_requests)
1043 std::ostringstream os(std::ios_base::binary);
1044 writeU16(os, TOSERVER_REQUEST_MEDIA);
1045 writeU16(os, file_requests.size());
1047 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1048 i != file_requests.end(); ++i) {
1049 os<<serializeString(i->name);
1053 std::string s = os.str();
1054 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1056 Send(0, data, true);
1057 infostream<<"Client: Sending media request list to server ("
1058 <<file_requests.size()<<" files)"<<std::endl;
1061 void Client::ReceiveAll()
1063 DSTACK(__FUNCTION_NAME);
1064 u32 start_ms = porting::getTimeMs();
1067 // Limit time even if there would be huge amounts of data to
1069 if(porting::getTimeMs() > start_ms + 100)
1074 g_profiler->graphAdd("client_received_packets", 1);
1076 catch(con::NoIncomingDataException &e)
1080 catch(con::InvalidIncomingDataException &e)
1082 infostream<<"Client::ReceiveAll(): "
1083 "InvalidIncomingDataException: what()="
1084 <<e.what()<<std::endl;
1089 void Client::Receive()
1091 DSTACK(__FUNCTION_NAME);
1092 SharedBuffer<u8> data;
1096 //TimeTaker t1("con mutex and receive", m_device);
1097 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1098 datasize = m_con.Receive(sender_peer_id, data);
1100 //TimeTaker t1("ProcessData", m_device);
1101 ProcessData(*data, datasize, sender_peer_id);
1105 sender_peer_id given to this shall be quaranteed to be a valid peer
1107 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1109 DSTACK(__FUNCTION_NAME);
1111 // Ignore packets that don't even fit a command
1114 m_packetcounter.add(60000);
1118 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1120 //infostream<<"Client: received command="<<command<<std::endl;
1121 m_packetcounter.add((u16)command);
1124 If this check is removed, be sure to change the queue
1125 system to know the ids
1127 if(sender_peer_id != PEER_ID_SERVER)
1129 infostream<<"Client::ProcessData(): Discarding data not "
1130 "coming from server: peer_id="<<sender_peer_id
1135 u8 ser_version = m_server_ser_ver;
1137 //infostream<<"Client received command="<<(int)command<<std::endl;
1139 if(command == TOCLIENT_INIT)
1144 u8 deployed = data[2];
1146 infostream<<"Client: TOCLIENT_INIT received with "
1147 "deployed="<<((int)deployed&0xff)<<std::endl;
1149 if(deployed < SER_FMT_VER_LOWEST
1150 || deployed > SER_FMT_VER_HIGHEST)
1152 infostream<<"Client: TOCLIENT_INIT: Server sent "
1153 <<"unsupported ser_fmt_ver"<<std::endl;
1157 m_server_ser_ver = deployed;
1159 // Get player position
1160 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1161 if(datasize >= 2+1+6)
1162 playerpos_s16 = readV3S16(&data[2+1]);
1163 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1166 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1168 // Set player position
1169 Player *player = m_env.getLocalPlayer();
1170 assert(player != NULL);
1171 player->setPosition(playerpos_f);
1174 if(datasize >= 2+1+6+8)
1177 m_map_seed = readU64(&data[2+1+6]);
1178 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1181 if(datasize >= 2+1+6+8+4)
1184 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1185 infostream<<"Client: received recommended send interval "
1186 <<m_recommended_send_interval<<std::endl;
1191 SharedBuffer<u8> reply(replysize);
1192 writeU16(&reply[0], TOSERVER_INIT2);
1194 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1199 if(command == TOCLIENT_ACCESS_DENIED)
1201 // The server didn't like our password. Note, this needs
1202 // to be processed even if the serialisation format has
1203 // not been agreed yet, the same as TOCLIENT_INIT.
1204 m_access_denied = true;
1205 m_access_denied_reason = L"Unknown";
1208 std::string datastring((char*)&data[2], datasize-2);
1209 std::istringstream is(datastring, std::ios_base::binary);
1210 m_access_denied_reason = deSerializeWideString(is);
1215 if(ser_version == SER_FMT_VER_INVALID)
1217 infostream<<"Client: Server serialization"
1218 " format invalid or not initialized."
1219 " Skipping incoming command="<<command<<std::endl;
1223 // Just here to avoid putting the two if's together when
1224 // making some copypasta
1227 if(command == TOCLIENT_REMOVENODE)
1232 p.X = readS16(&data[2]);
1233 p.Y = readS16(&data[4]);
1234 p.Z = readS16(&data[6]);
1236 //TimeTaker t1("TOCLIENT_REMOVENODE");
1240 else if(command == TOCLIENT_ADDNODE)
1242 if(datasize < 8 + MapNode::serializedLength(ser_version))
1246 p.X = readS16(&data[2]);
1247 p.Y = readS16(&data[4]);
1248 p.Z = readS16(&data[6]);
1250 //TimeTaker t1("TOCLIENT_ADDNODE");
1253 n.deSerialize(&data[8], ser_version);
1257 else if(command == TOCLIENT_BLOCKDATA)
1259 // Ignore too small packet
1264 p.X = readS16(&data[2]);
1265 p.Y = readS16(&data[4]);
1266 p.Z = readS16(&data[6]);
1268 /*infostream<<"Client: Thread: BLOCKDATA for ("
1269 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1270 /*infostream<<"Client: Thread: BLOCKDATA for ("
1271 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1273 std::string datastring((char*)&data[8], datasize-8);
1274 std::istringstream istr(datastring, std::ios_base::binary);
1279 v2s16 p2d(p.X, p.Z);
1280 sector = m_env.getMap().emergeSector(p2d);
1282 assert(sector->getPos() == p2d);
1284 //TimeTaker timer("MapBlock deSerialize");
1287 block = sector->getBlockNoCreateNoEx(p.Y);
1291 Update an existing block
1293 //infostream<<"Updating"<<std::endl;
1294 block->deSerialize(istr, ser_version, false);
1301 //infostream<<"Creating new"<<std::endl;
1302 block = new MapBlock(&m_env.getMap(), p, this);
1303 block->deSerialize(istr, ser_version, false);
1304 sector->insertBlock(block);
1318 u32 replysize = 2+1+6;
1319 SharedBuffer<u8> reply(replysize);
1320 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1322 writeV3S16(&reply[3], p);
1324 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1328 Add it to mesh update queue and set it to be acknowledged after update.
1330 //infostream<<"Adding mesh update task for received block"<<std::endl;
1331 addUpdateMeshTaskWithEdge(p, true);
1333 else if(command == TOCLIENT_INVENTORY)
1338 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1341 //TimeTaker t2("mutex locking", m_device);
1342 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1345 //TimeTaker t3("istringstream init", m_device);
1346 std::string datastring((char*)&data[2], datasize-2);
1347 std::istringstream is(datastring, std::ios_base::binary);
1350 //m_env.printPlayers(infostream);
1352 //TimeTaker t4("player get", m_device);
1353 Player *player = m_env.getLocalPlayer();
1354 assert(player != NULL);
1357 //TimeTaker t1("inventory.deSerialize()", m_device);
1358 player->inventory.deSerialize(is);
1361 m_inventory_updated = true;
1363 delete m_inventory_from_server;
1364 m_inventory_from_server = new Inventory(player->inventory);
1365 m_inventory_from_server_age = 0.0;
1367 //infostream<<"Client got player inventory:"<<std::endl;
1368 //player->inventory.print(infostream);
1371 else if(command == TOCLIENT_TIME_OF_DAY)
1376 u16 time_of_day = readU16(&data[2]);
1377 time_of_day = time_of_day % 24000;
1378 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1379 float time_speed = 0;
1380 if(datasize >= 2 + 2 + 4){
1381 time_speed = readF1000(&data[4]);
1383 // Old message; try to approximate speed of time by ourselves
1384 float time_of_day_f = (float)time_of_day / 24000.0;
1385 float tod_diff_f = 0;
1386 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1387 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1389 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1390 m_last_time_of_day_f = time_of_day_f;
1391 float time_diff = m_time_of_day_update_timer;
1392 m_time_of_day_update_timer = 0;
1393 if(m_time_of_day_set){
1394 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1395 infostream<<"Client: Measured time_of_day speed (old format): "
1396 <<time_speed<<" tod_diff_f="<<tod_diff_f
1397 <<" time_diff="<<time_diff<<std::endl;
1401 // Update environment
1402 m_env.setTimeOfDay(time_of_day);
1403 m_env.setTimeOfDaySpeed(time_speed);
1404 m_time_of_day_set = true;
1406 u32 dr = m_env.getDayNightRatio();
1407 verbosestream<<"Client: time_of_day="<<time_of_day
1408 <<" time_speed="<<time_speed
1409 <<" dr="<<dr<<std::endl;
1411 else if(command == TOCLIENT_CHAT_MESSAGE)
1419 std::string datastring((char*)&data[2], datasize-2);
1420 std::istringstream is(datastring, std::ios_base::binary);
1423 is.read((char*)buf, 2);
1424 u16 len = readU16(buf);
1426 std::wstring message;
1427 for(u16 i=0; i<len; i++)
1429 is.read((char*)buf, 2);
1430 message += (wchar_t)readU16(buf);
1433 /*infostream<<"Client received chat message: "
1434 <<wide_to_narrow(message)<<std::endl;*/
1436 m_chat_queue.push_back(message);
1438 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1440 //if(g_settings->getBool("enable_experimental"))
1444 u16 count of removed objects
1445 for all removed objects {
1448 u16 count of added objects
1449 for all added objects {
1452 u32 initialization data length
1453 string initialization data
1458 // Get all data except the command number
1459 std::string datastring((char*)&data[2], datasize-2);
1460 // Throw them in an istringstream
1461 std::istringstream is(datastring, std::ios_base::binary);
1465 // Read removed objects
1467 u16 removed_count = readU16((u8*)buf);
1468 for(u16 i=0; i<removed_count; i++)
1471 u16 id = readU16((u8*)buf);
1474 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1475 m_env.removeActiveObject(id);
1479 // Read added objects
1481 u16 added_count = readU16((u8*)buf);
1482 for(u16 i=0; i<added_count; i++)
1485 u16 id = readU16((u8*)buf);
1487 u8 type = readU8((u8*)buf);
1488 std::string data = deSerializeLongString(is);
1491 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1492 m_env.addActiveObject(id, type, data);
1497 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1499 //if(g_settings->getBool("enable_experimental"))
1511 // Get all data except the command number
1512 std::string datastring((char*)&data[2], datasize-2);
1513 // Throw them in an istringstream
1514 std::istringstream is(datastring, std::ios_base::binary);
1516 while(is.eof() == false)
1520 u16 id = readU16((u8*)buf);
1524 u16 message_size = readU16((u8*)buf);
1525 std::string message;
1526 message.reserve(message_size);
1527 for(u16 i=0; i<message_size; i++)
1530 message.append(buf, 1);
1532 // Pass on to the environment
1534 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1535 m_env.processActiveObjectMessage(id, message);
1540 else if(command == TOCLIENT_MOVEMENT)
1542 std::string datastring((char*)&data[2], datasize-2);
1543 std::istringstream is(datastring, std::ios_base::binary);
1544 Player *player = m_env.getLocalPlayer();
1545 assert(player != NULL);
1547 player->movement_acceleration_default = readF1000(is) * BS;
1548 player->movement_acceleration_air = readF1000(is) * BS;
1549 player->movement_acceleration_fast = readF1000(is) * BS;
1550 player->movement_speed_walk = readF1000(is) * BS;
1551 player->movement_speed_crouch = readF1000(is) * BS;
1552 player->movement_speed_fast = readF1000(is) * BS;
1553 player->movement_speed_climb = readF1000(is) * BS;
1554 player->movement_speed_jump = readF1000(is) * BS;
1555 player->movement_liquid_fluidity = readF1000(is) * BS;
1556 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1557 player->movement_liquid_sink = readF1000(is) * BS;
1558 player->movement_gravity = readF1000(is) * BS;
1560 else if(command == TOCLIENT_HP)
1562 std::string datastring((char*)&data[2], datasize-2);
1563 std::istringstream is(datastring, std::ios_base::binary);
1564 Player *player = m_env.getLocalPlayer();
1565 assert(player != NULL);
1566 u8 oldhp = player->hp;
1572 // Add to ClientEvent queue
1574 event.type = CE_PLAYER_DAMAGE;
1575 event.player_damage.amount = oldhp - hp;
1576 m_client_event_queue.push_back(event);
1579 else if(command == TOCLIENT_MOVE_PLAYER)
1581 std::string datastring((char*)&data[2], datasize-2);
1582 std::istringstream is(datastring, std::ios_base::binary);
1583 Player *player = m_env.getLocalPlayer();
1584 assert(player != NULL);
1585 v3f pos = readV3F1000(is);
1586 f32 pitch = readF1000(is);
1587 f32 yaw = readF1000(is);
1588 player->setPosition(pos);
1589 /*player->setPitch(pitch);
1590 player->setYaw(yaw);*/
1592 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1593 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1599 Add to ClientEvent queue.
1600 This has to be sent to the main program because otherwise
1601 it would just force the pitch and yaw values to whatever
1602 the camera points to.
1605 event.type = CE_PLAYER_FORCE_MOVE;
1606 event.player_force_move.pitch = pitch;
1607 event.player_force_move.yaw = yaw;
1608 m_client_event_queue.push_back(event);
1610 // Ignore damage for a few seconds, so that the player doesn't
1611 // get damage from falling on ground
1612 m_ignore_damage_timer = 3.0;
1614 else if(command == TOCLIENT_PLAYERITEM)
1616 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1618 else if(command == TOCLIENT_DEATHSCREEN)
1620 std::string datastring((char*)&data[2], datasize-2);
1621 std::istringstream is(datastring, std::ios_base::binary);
1623 bool set_camera_point_target = readU8(is);
1624 v3f camera_point_target = readV3F1000(is);
1627 event.type = CE_DEATHSCREEN;
1628 event.deathscreen.set_camera_point_target = set_camera_point_target;
1629 event.deathscreen.camera_point_target_x = camera_point_target.X;
1630 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1631 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1632 m_client_event_queue.push_back(event);
1634 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1636 std::string datastring((char*)&data[2], datasize-2);
1637 std::istringstream is(datastring, std::ios_base::binary);
1639 // Mesh update thread must be stopped while
1640 // updating content definitions
1641 assert(!m_mesh_update_thread.IsRunning());
1643 int num_files = readU16(is);
1645 infostream<<"Client: Received media announcement: packet size: "
1646 <<datasize<<std::endl;
1648 std::list<MediaRequest> file_requests;
1650 for(int i=0; i<num_files; i++)
1652 //read file from cache
1653 std::string name = deSerializeString(is);
1654 std::string sha1_base64 = deSerializeString(is);
1656 // if name contains illegal characters, ignore the file
1657 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1658 errorstream<<"Client: ignoring illegal file name "
1659 <<"sent by server: \""<<name<<"\""<<std::endl;
1663 std::string sha1_raw = base64_decode(sha1_base64);
1664 std::string sha1_hex = hex_encode(sha1_raw);
1665 std::ostringstream tmp_os(std::ios_base::binary);
1666 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1667 m_media_name_sha1_map[name] = sha1_raw;
1669 // If found in cache, try to load it from there
1672 bool success = loadMedia(tmp_os.str(), name);
1674 verbosestream<<"Client: Loaded cached media: "
1675 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1678 infostream<<"Client: Failed to load cached media: "
1679 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1682 // Didn't load from cache; queue it to be requested
1683 verbosestream<<"Client: Adding file to request list: \""
1684 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1685 file_requests.push_back(MediaRequest(name));
1688 std::string remote_media = "";
1690 remote_media = deSerializeString(is);
1692 catch(SerializationError) {
1693 // not supported by server or turned off
1696 m_media_count = file_requests.size();
1697 m_media_receive_started = true;
1699 if (remote_media == "" || !USE_CURL) {
1700 request_media(file_requests);
1703 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1704 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1705 i != file_requests.end(); ++i) {
1706 (*cur)->m_file_requests.push_back(*i);
1708 if (cur == m_media_fetch_threads.end())
1709 cur = m_media_fetch_threads.begin();
1711 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1712 i != m_media_fetch_threads.end(); ++i) {
1713 (*i)->m_remote_url = remote_media;
1718 // notify server we received everything
1719 std::ostringstream os(std::ios_base::binary);
1720 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1721 std::string s = os.str();
1722 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1724 Send(0, data, true);
1727 event.type = CE_TEXTURES_UPDATED;
1728 m_client_event_queue.push_back(event);
1730 else if(command == TOCLIENT_MEDIA)
1732 if (m_media_count == 0)
1734 std::string datastring((char*)&data[2], datasize-2);
1735 std::istringstream is(datastring, std::ios_base::binary);
1737 // Mesh update thread must be stopped while
1738 // updating content definitions
1739 assert(!m_mesh_update_thread.IsRunning());
1743 u16 total number of file bunches
1744 u16 index of this bunch
1745 u32 number of files in this bunch
1753 int num_bunches = readU16(is);
1754 int bunch_i = readU16(is);
1755 int num_files = readU32(is);
1756 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1757 <<num_bunches<<" files="<<num_files
1758 <<" size="<<datasize<<std::endl;
1759 for(int i=0; i<num_files; i++){
1760 m_media_received_count++;
1761 std::string name = deSerializeString(is);
1762 std::string data = deSerializeLongString(is);
1764 // if name contains illegal characters, ignore the file
1765 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1766 errorstream<<"Client: ignoring illegal file name "
1767 <<"sent by server: \""<<name<<"\""<<std::endl;
1771 bool success = loadMedia(data, name);
1773 verbosestream<<"Client: Loaded received media: "
1774 <<"\""<<name<<"\". Caching."<<std::endl;
1776 infostream<<"Client: Failed to load received media: "
1777 <<"\""<<name<<"\". Not caching."<<std::endl;
1781 bool did = fs::CreateAllDirs(getMediaCacheDir());
1783 errorstream<<"Could not create media cache directory"
1788 std::map<std::string, std::string>::iterator n;
1789 n = m_media_name_sha1_map.find(name);
1790 if(n == m_media_name_sha1_map.end())
1791 errorstream<<"The server sent a file that has not "
1792 <<"been announced."<<std::endl;
1794 m_media_cache.update_sha1(data);
1799 event.type = CE_TEXTURES_UPDATED;
1800 m_client_event_queue.push_back(event);
1802 else if(command == TOCLIENT_TOOLDEF)
1804 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1806 else if(command == TOCLIENT_NODEDEF)
1808 infostream<<"Client: Received node definitions: packet size: "
1809 <<datasize<<std::endl;
1811 // Mesh update thread must be stopped while
1812 // updating content definitions
1813 assert(!m_mesh_update_thread.IsRunning());
1815 // Decompress node definitions
1816 std::string datastring((char*)&data[2], datasize-2);
1817 std::istringstream is(datastring, std::ios_base::binary);
1818 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1819 std::ostringstream tmp_os;
1820 decompressZlib(tmp_is, tmp_os);
1822 // Deserialize node definitions
1823 std::istringstream tmp_is2(tmp_os.str());
1824 m_nodedef->deSerialize(tmp_is2);
1825 m_nodedef_received = true;
1827 else if(command == TOCLIENT_CRAFTITEMDEF)
1829 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1831 else if(command == TOCLIENT_ITEMDEF)
1833 infostream<<"Client: Received item definitions: packet size: "
1834 <<datasize<<std::endl;
1836 // Mesh update thread must be stopped while
1837 // updating content definitions
1838 assert(!m_mesh_update_thread.IsRunning());
1840 // Decompress item definitions
1841 std::string datastring((char*)&data[2], datasize-2);
1842 std::istringstream is(datastring, std::ios_base::binary);
1843 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1844 std::ostringstream tmp_os;
1845 decompressZlib(tmp_is, tmp_os);
1847 // Deserialize node definitions
1848 std::istringstream tmp_is2(tmp_os.str());
1849 m_itemdef->deSerialize(tmp_is2);
1850 m_itemdef_received = true;
1852 else if(command == TOCLIENT_PLAY_SOUND)
1854 std::string datastring((char*)&data[2], datasize-2);
1855 std::istringstream is(datastring, std::ios_base::binary);
1857 s32 server_id = readS32(is);
1858 std::string name = deSerializeString(is);
1859 float gain = readF1000(is);
1860 int type = readU8(is); // 0=local, 1=positional, 2=object
1861 v3f pos = readV3F1000(is);
1862 u16 object_id = readU16(is);
1863 bool loop = readU8(is);
1868 client_id = m_sound->playSound(name, loop, gain);
1870 case 1: // positional
1871 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1874 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1876 pos = cao->getPosition();
1877 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1878 // TODO: Set up sound to move with object
1883 if(client_id != -1){
1884 m_sounds_server_to_client[server_id] = client_id;
1885 m_sounds_client_to_server[client_id] = server_id;
1887 m_sounds_to_objects[client_id] = object_id;
1890 else if(command == TOCLIENT_STOP_SOUND)
1892 std::string datastring((char*)&data[2], datasize-2);
1893 std::istringstream is(datastring, std::ios_base::binary);
1895 s32 server_id = readS32(is);
1896 std::map<s32, int>::iterator i =
1897 m_sounds_server_to_client.find(server_id);
1898 if(i != m_sounds_server_to_client.end()){
1899 int client_id = i->second;
1900 m_sound->stopSound(client_id);
1903 else if(command == TOCLIENT_PRIVILEGES)
1905 std::string datastring((char*)&data[2], datasize-2);
1906 std::istringstream is(datastring, std::ios_base::binary);
1908 m_privileges.clear();
1909 infostream<<"Client: Privileges updated: ";
1910 u16 num_privileges = readU16(is);
1911 for(u16 i=0; i<num_privileges; i++){
1912 std::string priv = deSerializeString(is);
1913 m_privileges.insert(priv);
1914 infostream<<priv<<" ";
1916 infostream<<std::endl;
1918 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1920 std::string datastring((char*)&data[2], datasize-2);
1921 std::istringstream is(datastring, std::ios_base::binary);
1923 // Store formspec in LocalPlayer
1924 Player *player = m_env.getLocalPlayer();
1925 assert(player != NULL);
1926 player->inventory_formspec = deSerializeLongString(is);
1928 else if(command == TOCLIENT_DETACHED_INVENTORY)
1930 std::string datastring((char*)&data[2], datasize-2);
1931 std::istringstream is(datastring, std::ios_base::binary);
1933 std::string name = deSerializeString(is);
1935 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1937 Inventory *inv = NULL;
1938 if(m_detached_inventories.count(name) > 0)
1939 inv = m_detached_inventories[name];
1941 inv = new Inventory(m_itemdef);
1942 m_detached_inventories[name] = inv;
1944 inv->deSerialize(is);
1946 else if(command == TOCLIENT_SHOW_FORMSPEC)
1948 std::string datastring((char*)&data[2], datasize-2);
1949 std::istringstream is(datastring, std::ios_base::binary);
1951 std::string formspec = deSerializeLongString(is);
1952 std::string formname = deSerializeString(is);
1955 event.type = CE_SHOW_FORMSPEC;
1956 // pointer is required as event is a struct only!
1957 // adding a std:string to a struct isn't possible
1958 event.show_formspec.formspec = new std::string(formspec);
1959 event.show_formspec.formname = new std::string(formname);
1960 m_client_event_queue.push_back(event);
1962 else if(command == TOCLIENT_SPAWN_PARTICLE)
1964 std::string datastring((char*)&data[2], datasize-2);
1965 std::istringstream is(datastring, std::ios_base::binary);
1967 v3f pos = readV3F1000(is);
1968 v3f vel = readV3F1000(is);
1969 v3f acc = readV3F1000(is);
1970 float expirationtime = readF1000(is);
1971 float size = readF1000(is);
1972 bool collisiondetection = readU8(is);
1973 std::string texture = deSerializeLongString(is);
1976 event.type = CE_SPAWN_PARTICLE;
1977 event.spawn_particle.pos = new v3f (pos);
1978 event.spawn_particle.vel = new v3f (vel);
1979 event.spawn_particle.acc = new v3f (acc);
1981 event.spawn_particle.expirationtime = expirationtime;
1982 event.spawn_particle.size = size;
1983 event.add_particlespawner.collisiondetection =
1985 event.spawn_particle.texture = new std::string(texture);
1987 m_client_event_queue.push_back(event);
1989 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1991 std::string datastring((char*)&data[2], datasize-2);
1992 std::istringstream is(datastring, std::ios_base::binary);
1994 u16 amount = readU16(is);
1995 float spawntime = readF1000(is);
1996 v3f minpos = readV3F1000(is);
1997 v3f maxpos = readV3F1000(is);
1998 v3f minvel = readV3F1000(is);
1999 v3f maxvel = readV3F1000(is);
2000 v3f minacc = readV3F1000(is);
2001 v3f maxacc = readV3F1000(is);
2002 float minexptime = readF1000(is);
2003 float maxexptime = readF1000(is);
2004 float minsize = readF1000(is);
2005 float maxsize = readF1000(is);
2006 bool collisiondetection = readU8(is);
2007 std::string texture = deSerializeLongString(is);
2008 u32 id = readU32(is);
2011 event.type = CE_ADD_PARTICLESPAWNER;
2012 event.add_particlespawner.amount = amount;
2013 event.add_particlespawner.spawntime = spawntime;
2015 event.add_particlespawner.minpos = new v3f (minpos);
2016 event.add_particlespawner.maxpos = new v3f (maxpos);
2017 event.add_particlespawner.minvel = new v3f (minvel);
2018 event.add_particlespawner.maxvel = new v3f (maxvel);
2019 event.add_particlespawner.minacc = new v3f (minacc);
2020 event.add_particlespawner.maxacc = new v3f (maxacc);
2022 event.add_particlespawner.minexptime = minexptime;
2023 event.add_particlespawner.maxexptime = maxexptime;
2024 event.add_particlespawner.minsize = minsize;
2025 event.add_particlespawner.maxsize = maxsize;
2026 event.add_particlespawner.collisiondetection = collisiondetection;
2027 event.add_particlespawner.texture = new std::string(texture);
2028 event.add_particlespawner.id = id;
2030 m_client_event_queue.push_back(event);
2032 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2034 std::string datastring((char*)&data[2], datasize-2);
2035 std::istringstream is(datastring, std::ios_base::binary);
2037 u32 id = readU16(is);
2040 event.type = CE_DELETE_PARTICLESPAWNER;
2041 event.delete_particlespawner.id = id;
2043 m_client_event_queue.push_back(event);
2045 else if(command == TOCLIENT_HUDADD)
2047 std::string datastring((char *)&data[2], datasize - 2);
2048 std::istringstream is(datastring, std::ios_base::binary);
2050 u32 id = readU32(is);
2051 u8 type = readU8(is);
2052 v2f pos = readV2F1000(is);
2053 std::string name = deSerializeString(is);
2054 v2f scale = readV2F1000(is);
2055 std::string text = deSerializeString(is);
2056 u32 number = readU32(is);
2057 u32 item = readU32(is);
2058 u32 dir = readU32(is);
2059 v2f align = readV2F1000(is);
2060 v2f offset = readV2F1000(is);
2063 event.type = CE_HUDADD;
2064 event.hudadd.id = id;
2065 event.hudadd.type = type;
2066 event.hudadd.pos = new v2f(pos);
2067 event.hudadd.name = new std::string(name);
2068 event.hudadd.scale = new v2f(scale);
2069 event.hudadd.text = new std::string(text);
2070 event.hudadd.number = number;
2071 event.hudadd.item = item;
2072 event.hudadd.dir = dir;
2073 event.hudadd.align = new v2f(align);
2074 event.hudadd.offset = new v2f(offset);
2075 m_client_event_queue.push_back(event);
2077 else if(command == TOCLIENT_HUDRM)
2079 std::string datastring((char *)&data[2], datasize - 2);
2080 std::istringstream is(datastring, std::ios_base::binary);
2082 u32 id = readU32(is);
2085 event.type = CE_HUDRM;
2086 event.hudrm.id = id;
2087 m_client_event_queue.push_back(event);
2089 else if(command == TOCLIENT_HUDCHANGE)
2095 std::string datastring((char *)&data[2], datasize - 2);
2096 std::istringstream is(datastring, std::ios_base::binary);
2098 u32 id = readU32(is);
2099 u8 stat = (HudElementStat)readU8(is);
2101 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2102 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2103 v2fdata = readV2F1000(is);
2104 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2105 sdata = deSerializeString(is);
2107 intdata = readU32(is);
2110 event.type = CE_HUDCHANGE;
2111 event.hudchange.id = id;
2112 event.hudchange.stat = (HudElementStat)stat;
2113 event.hudchange.v2fdata = new v2f(v2fdata);
2114 event.hudchange.sdata = new std::string(sdata);
2115 event.hudchange.data = intdata;
2116 m_client_event_queue.push_back(event);
2118 else if(command == TOCLIENT_HUD_SET_FLAGS)
2120 std::string datastring((char *)&data[2], datasize - 2);
2121 std::istringstream is(datastring, std::ios_base::binary);
2123 Player *player = m_env.getLocalPlayer();
2124 assert(player != NULL);
2126 u32 flags = readU32(is);
2127 u32 mask = readU32(is);
2129 player->hud_flags &= ~mask;
2130 player->hud_flags |= flags;
2134 infostream<<"Client: Ignoring unknown command "
2135 <<command<<std::endl;
2139 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2141 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2142 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2145 void Client::interact(u8 action, const PointedThing& pointed)
2147 if(connectedAndInitialized() == false){
2148 infostream<<"Client::interact() "
2149 "cancelled (not connected)"
2154 std::ostringstream os(std::ios_base::binary);
2160 [5] u32 length of the next item
2161 [9] serialized PointedThing
2163 0: start digging (from undersurface) or use
2164 1: stop digging (all parameters ignored)
2165 2: digging completed
2166 3: place block or item (to abovesurface)
2169 writeU16(os, TOSERVER_INTERACT);
2170 writeU8(os, action);
2171 writeU16(os, getPlayerItem());
2172 std::ostringstream tmp_os(std::ios::binary);
2173 pointed.serialize(tmp_os);
2174 os<<serializeLongString(tmp_os.str());
2176 std::string s = os.str();
2177 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2180 Send(0, data, true);
2183 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2184 const std::map<std::string, std::string> &fields)
2186 std::ostringstream os(std::ios_base::binary);
2188 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2190 os<<serializeString(formname);
2191 writeU16(os, fields.size());
2192 for(std::map<std::string, std::string>::const_iterator
2193 i = fields.begin(); i != fields.end(); i++){
2194 const std::string &name = i->first;
2195 const std::string &value = i->second;
2196 os<<serializeString(name);
2197 os<<serializeLongString(value);
2201 std::string s = os.str();
2202 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2204 Send(0, data, true);
2207 void Client::sendInventoryFields(const std::string &formname,
2208 const std::map<std::string, std::string> &fields)
2210 std::ostringstream os(std::ios_base::binary);
2212 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2213 os<<serializeString(formname);
2214 writeU16(os, fields.size());
2215 for(std::map<std::string, std::string>::const_iterator
2216 i = fields.begin(); i != fields.end(); i++){
2217 const std::string &name = i->first;
2218 const std::string &value = i->second;
2219 os<<serializeString(name);
2220 os<<serializeLongString(value);
2224 std::string s = os.str();
2225 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2227 Send(0, data, true);
2230 void Client::sendInventoryAction(InventoryAction *a)
2232 std::ostringstream os(std::ios_base::binary);
2236 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2237 os.write((char*)buf, 2);
2242 std::string s = os.str();
2243 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2245 Send(0, data, true);
2248 void Client::sendChatMessage(const std::wstring &message)
2250 std::ostringstream os(std::ios_base::binary);
2254 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2255 os.write((char*)buf, 2);
2258 writeU16(buf, message.size());
2259 os.write((char*)buf, 2);
2262 for(u32 i=0; i<message.size(); i++)
2266 os.write((char*)buf, 2);
2270 std::string s = os.str();
2271 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2273 Send(0, data, true);
2276 void Client::sendChangePassword(const std::wstring oldpassword,
2277 const std::wstring newpassword)
2279 Player *player = m_env.getLocalPlayer();
2283 std::string playername = player->getName();
2284 std::string oldpwd = translatePassword(playername, oldpassword);
2285 std::string newpwd = translatePassword(playername, newpassword);
2287 std::ostringstream os(std::ios_base::binary);
2288 u8 buf[2+PASSWORD_SIZE*2];
2290 [0] u16 TOSERVER_PASSWORD
2291 [2] u8[28] old password
2292 [30] u8[28] new password
2295 writeU16(buf, TOSERVER_PASSWORD);
2296 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2298 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2299 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2301 buf[2+PASSWORD_SIZE-1] = 0;
2302 buf[30+PASSWORD_SIZE-1] = 0;
2303 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2306 std::string s = os.str();
2307 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2309 Send(0, data, true);
2313 void Client::sendDamage(u8 damage)
2315 DSTACK(__FUNCTION_NAME);
2316 std::ostringstream os(std::ios_base::binary);
2318 writeU16(os, TOSERVER_DAMAGE);
2319 writeU8(os, damage);
2322 std::string s = os.str();
2323 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2325 Send(0, data, true);
2328 void Client::sendRespawn()
2330 DSTACK(__FUNCTION_NAME);
2331 std::ostringstream os(std::ios_base::binary);
2333 writeU16(os, TOSERVER_RESPAWN);
2336 std::string s = os.str();
2337 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2339 Send(0, data, true);
2342 void Client::sendPlayerPos()
2344 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2346 LocalPlayer *myplayer = m_env.getLocalPlayer();
2347 if(myplayer == NULL)
2350 // Save bandwidth by only updating position when something changed
2351 if(myplayer->last_position == myplayer->getPosition() &&
2352 myplayer->last_speed == myplayer->getSpeed() &&
2353 myplayer->last_pitch == myplayer->getPitch() &&
2354 myplayer->last_yaw == myplayer->getYaw() &&
2355 myplayer->last_keyPressed == myplayer->keyPressed)
2358 myplayer->last_position = myplayer->getPosition();
2359 myplayer->last_speed = myplayer->getSpeed();
2360 myplayer->last_pitch = myplayer->getPitch();
2361 myplayer->last_yaw = myplayer->getYaw();
2362 myplayer->last_keyPressed = myplayer->keyPressed;
2366 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2367 our_peer_id = m_con.GetPeerID();
2370 // Set peer id if not set already
2371 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2372 myplayer->peer_id = our_peer_id;
2373 // Check that an existing peer_id is the same as the connection's
2374 assert(myplayer->peer_id == our_peer_id);
2376 v3f pf = myplayer->getPosition();
2377 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2378 v3f sf = myplayer->getSpeed();
2379 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2380 s32 pitch = myplayer->getPitch() * 100;
2381 s32 yaw = myplayer->getYaw() * 100;
2382 u32 keyPressed=myplayer->keyPressed;
2386 [2] v3s32 position*100
2387 [2+12] v3s32 speed*100
2388 [2+12+12] s32 pitch*100
2389 [2+12+12+4] s32 yaw*100
2390 [2+12+12+4+4] u32 keyPressed
2392 SharedBuffer<u8> data(2+12+12+4+4+4);
2393 writeU16(&data[0], TOSERVER_PLAYERPOS);
2394 writeV3S32(&data[2], position);
2395 writeV3S32(&data[2+12], speed);
2396 writeS32(&data[2+12+12], pitch);
2397 writeS32(&data[2+12+12+4], yaw);
2398 writeU32(&data[2+12+12+4+4], keyPressed);
2399 // Send as unreliable
2400 Send(0, data, false);
2403 void Client::sendPlayerItem(u16 item)
2405 Player *myplayer = m_env.getLocalPlayer();
2406 if(myplayer == NULL)
2409 u16 our_peer_id = m_con.GetPeerID();
2411 // Set peer id if not set already
2412 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2413 myplayer->peer_id = our_peer_id;
2414 // Check that an existing peer_id is the same as the connection's
2415 assert(myplayer->peer_id == our_peer_id);
2417 SharedBuffer<u8> data(2+2);
2418 writeU16(&data[0], TOSERVER_PLAYERITEM);
2419 writeU16(&data[2], item);
2422 Send(0, data, true);
2425 void Client::removeNode(v3s16 p)
2427 std::map<v3s16, MapBlock*> modified_blocks;
2431 //TimeTaker t("removeNodeAndUpdate", m_device);
2432 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2434 catch(InvalidPositionException &e)
2438 // add urgent task to update the modified node
2439 addUpdateMeshTaskForNode(p, false, true);
2441 for(std::map<v3s16, MapBlock * >::iterator
2442 i = modified_blocks.begin();
2443 i != modified_blocks.end(); ++i)
2445 addUpdateMeshTaskWithEdge(i->first);
2449 void Client::addNode(v3s16 p, MapNode n)
2451 TimeTaker timer1("Client::addNode()");
2453 std::map<v3s16, MapBlock*> modified_blocks;
2457 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2458 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2460 catch(InvalidPositionException &e)
2463 for(std::map<v3s16, MapBlock * >::iterator
2464 i = modified_blocks.begin();
2465 i != modified_blocks.end(); ++i)
2467 addUpdateMeshTaskWithEdge(i->first);
2471 void Client::setPlayerControl(PlayerControl &control)
2473 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2474 LocalPlayer *player = m_env.getLocalPlayer();
2475 assert(player != NULL);
2476 player->control = control;
2479 void Client::selectPlayerItem(u16 item)
2481 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2482 m_playeritem = item;
2483 m_inventory_updated = true;
2484 sendPlayerItem(item);
2487 // Returns true if the inventory of the local player has been
2488 // updated from the server. If it is true, it is set to false.
2489 bool Client::getLocalInventoryUpdated()
2491 // m_inventory_updated is behind envlock
2492 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2493 bool updated = m_inventory_updated;
2494 m_inventory_updated = false;
2498 // Copies the inventory of the local player to parameter
2499 void Client::getLocalInventory(Inventory &dst)
2501 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2502 Player *player = m_env.getLocalPlayer();
2503 assert(player != NULL);
2504 dst = player->inventory;
2507 Inventory* Client::getInventory(const InventoryLocation &loc)
2510 case InventoryLocation::UNDEFINED:
2513 case InventoryLocation::CURRENT_PLAYER:
2515 Player *player = m_env.getLocalPlayer();
2516 assert(player != NULL);
2517 return &player->inventory;
2520 case InventoryLocation::PLAYER:
2522 Player *player = m_env.getPlayer(loc.name.c_str());
2525 return &player->inventory;
2528 case InventoryLocation::NODEMETA:
2530 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2533 return meta->getInventory();
2536 case InventoryLocation::DETACHED:
2538 if(m_detached_inventories.count(loc.name) == 0)
2540 return m_detached_inventories[loc.name];
2548 void Client::inventoryAction(InventoryAction *a)
2551 Send it to the server
2553 sendInventoryAction(a);
2556 Predict some local inventory changes
2558 a->clientApply(this, this);
2564 ClientActiveObject * Client::getSelectedActiveObject(
2566 v3f from_pos_f_on_map,
2567 core::line3d<f32> shootline_on_map
2570 std::vector<DistanceSortedActiveObject> objects;
2572 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2574 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2577 // After this, the closest object is the first in the array.
2578 std::sort(objects.begin(), objects.end());
2580 for(u32 i=0; i<objects.size(); i++)
2582 ClientActiveObject *obj = objects[i].obj;
2584 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2585 if(selection_box == NULL)
2588 v3f pos = obj->getPosition();
2590 core::aabbox3d<f32> offsetted_box(
2591 selection_box->MinEdge + pos,
2592 selection_box->MaxEdge + pos
2595 if(offsetted_box.intersectsWithLine(shootline_on_map))
2597 //infostream<<"Returning selected object"<<std::endl;
2602 //infostream<<"No object selected; returning NULL."<<std::endl;
2606 void Client::printDebugInfo(std::ostream &os)
2608 //JMutexAutoLock lock1(m_fetchblock_mutex);
2609 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2611 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2612 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2613 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2617 std::list<std::string> Client::getConnectedPlayerNames()
2619 return m_env.getPlayerNames();
2622 float Client::getAnimationTime()
2624 return m_animation_time;
2627 int Client::getCrackLevel()
2629 return m_crack_level;
2632 void Client::setCrack(int level, v3s16 pos)
2634 int old_crack_level = m_crack_level;
2635 v3s16 old_crack_pos = m_crack_pos;
2637 m_crack_level = level;
2640 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2643 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2645 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2648 addUpdateMeshTaskForNode(pos, false, true);
2654 Player *player = m_env.getLocalPlayer();
2655 assert(player != NULL);
2659 bool Client::getChatMessage(std::wstring &message)
2661 if(m_chat_queue.size() == 0)
2663 message = m_chat_queue.pop_front();
2667 void Client::typeChatMessage(const std::wstring &message)
2669 // Discard empty line
2674 sendChatMessage(message);
2677 if (message[0] == L'/')
2679 m_chat_queue.push_back(
2680 (std::wstring)L"issued command: "+message);
2684 LocalPlayer *player = m_env.getLocalPlayer();
2685 assert(player != NULL);
2686 std::wstring name = narrow_to_wide(player->getName());
2687 m_chat_queue.push_back(
2688 (std::wstring)L"<"+name+L"> "+message);
2692 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2694 /*infostream<<"Client::addUpdateMeshTask(): "
2695 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2696 <<" ack_to_server="<<ack_to_server
2697 <<" urgent="<<urgent
2700 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2705 Create a task to update the mesh of the block
2708 MeshMakeData *data = new MeshMakeData(this);
2711 //TimeTaker timer("data fill");
2713 // Debug: 1-6ms, avg=2ms
2715 data->setCrack(m_crack_level, m_crack_pos);
2716 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2720 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2722 // Add task to queue
2723 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2725 /*infostream<<"Mesh update input queue size is "
2726 <<m_mesh_update_thread.m_queue_in.size()
2730 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2734 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2735 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2740 v3s16 p = blockpos + v3s16(0,0,0);
2741 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2742 addUpdateMeshTask(p, ack_to_server, urgent);
2744 catch(InvalidPositionException &e){}
2746 for (int i=0;i<6;i++)
2749 v3s16 p = blockpos + g_6dirs[i];
2750 addUpdateMeshTask(p, false, urgent);
2752 catch(InvalidPositionException &e){}
2756 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2760 infostream<<"Client::addUpdateMeshTaskForNode(): "
2761 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2765 v3s16 blockpos = getNodeBlockPos(nodepos);
2766 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2769 v3s16 p = blockpos + v3s16(0,0,0);
2770 addUpdateMeshTask(p, ack_to_server, urgent);
2772 catch(InvalidPositionException &e){}
2774 if(nodepos.X == blockpos_relative.X){
2776 v3s16 p = blockpos + v3s16(-1,0,0);
2777 addUpdateMeshTask(p, false, urgent);
2779 catch(InvalidPositionException &e){}
2781 if(nodepos.Y == blockpos_relative.Y){
2783 v3s16 p = blockpos + v3s16(0,-1,0);
2784 addUpdateMeshTask(p, false, urgent);
2786 catch(InvalidPositionException &e){}
2788 if(nodepos.Z == blockpos_relative.Z){
2790 v3s16 p = blockpos + v3s16(0,0,-1);
2791 addUpdateMeshTask(p, false, urgent);
2793 catch(InvalidPositionException &e){}
2797 ClientEvent Client::getClientEvent()
2799 if(m_client_event_queue.size() == 0)
2802 event.type = CE_NONE;
2805 return m_client_event_queue.pop_front();
2808 void draw_load_screen(const std::wstring &text,
2809 IrrlichtDevice* device, gui::IGUIFont* font,
2810 float dtime=0 ,int percent=0, bool clouds=true);
2811 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2813 infostream<<"Client::afterContentReceived() started"<<std::endl;
2814 assert(m_itemdef_received);
2815 assert(m_nodedef_received);
2816 assert(texturesReceived());
2818 // remove the information about which checksum each texture
2820 m_media_name_sha1_map.clear();
2822 // Rebuild inherited images and recreate textures
2823 infostream<<"- Rebuilding images and textures"<<std::endl;
2824 m_tsrc->rebuildImagesAndTextures();
2826 // Update texture atlas
2827 infostream<<"- Updating texture atlas"<<std::endl;
2828 if(g_settings->getBool("enable_texture_atlas"))
2829 m_tsrc->buildMainAtlas(this);
2832 m_shsrc->rebuildShaders();
2834 // Update node aliases
2835 infostream<<"- Updating node aliases"<<std::endl;
2836 m_nodedef->updateAliases(m_itemdef);
2838 // Update node textures
2839 infostream<<"- Updating node textures"<<std::endl;
2840 m_nodedef->updateTextures(m_tsrc);
2842 // Preload item textures and meshes if configured to
2843 if(g_settings->getBool("preload_item_visuals"))
2845 verbosestream<<"Updating item textures and meshes"<<std::endl;
2846 wchar_t* text = wgettext("Item textures...");
2847 draw_load_screen(text,device,font,0,0);
2848 std::set<std::string> names = m_itemdef->getAll();
2849 size_t size = names.size();
2852 for(std::set<std::string>::const_iterator
2853 i = names.begin(); i != names.end(); ++i){
2854 // Asking for these caches the result
2855 m_itemdef->getInventoryTexture(*i, this);
2856 m_itemdef->getWieldMesh(*i, this);
2858 percent = count*100/size;
2859 if (count%50 == 0) // only update every 50 item
2860 draw_load_screen(text,device,font,0,percent);
2865 // Start mesh update thread after setting up content definitions
2866 infostream<<"- Starting mesh update thread"<<std::endl;
2867 m_mesh_update_thread.Start();
2869 infostream<<"Client::afterContentReceived() done"<<std::endl;
2872 float Client::getRTT(void)
2875 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2876 } catch(con::PeerNotFoundException &e){
2881 // IGameDef interface
2883 IItemDefManager* Client::getItemDefManager()
2887 INodeDefManager* Client::getNodeDefManager()
2891 ICraftDefManager* Client::getCraftDefManager()
2894 //return m_craftdef;
2896 ITextureSource* Client::getTextureSource()
2900 IShaderSource* Client::getShaderSource()
2904 u16 Client::allocateUnknownNodeId(const std::string &name)
2906 errorstream<<"Client::allocateUnknownNodeId(): "
2907 <<"Client cannot allocate node IDs"<<std::endl;
2909 return CONTENT_IGNORE;
2911 ISoundManager* Client::getSoundManager()
2915 MtEventManager* Client::getEventManager()