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"
28 #include "mapsector.h"
29 #include "mapblock_mesh.h"
35 #include "nodemetadata.h"
39 #include <IFileSystem.h>
42 #include "clientmap.h"
43 #include "filecache.h"
45 #include "util/string.h"
47 #include "IMeshCache.h"
48 #include "util/serialize.h"
50 #include "util/directiontables.h"
53 #include <curl/curl.h>
56 static std::string getMediaCacheDir()
58 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
65 QueuedMeshUpdate::QueuedMeshUpdate():
68 ack_block_to_server(false)
72 QueuedMeshUpdate::~QueuedMeshUpdate()
82 MeshUpdateQueue::MeshUpdateQueue()
87 MeshUpdateQueue::~MeshUpdateQueue()
89 JMutexAutoLock lock(m_mutex);
91 for(std::vector<QueuedMeshUpdate*>::iterator
93 i != m_queue.end(); i++)
95 QueuedMeshUpdate *q = *i;
101 peer_id=0 adds with nobody to send to
103 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
105 DSTACK(__FUNCTION_NAME);
109 JMutexAutoLock lock(m_mutex);
115 Find if block is already in queue.
116 If it is, update the data and quit.
118 for(std::vector<QueuedMeshUpdate*>::iterator
120 i != m_queue.end(); i++)
122 QueuedMeshUpdate *q = *i;
128 if(ack_block_to_server)
129 q->ack_block_to_server = true;
137 QueuedMeshUpdate *q = new QueuedMeshUpdate;
140 q->ack_block_to_server = ack_block_to_server;
141 m_queue.push_back(q);
144 // Returned pointer must be deleted
145 // Returns NULL if queue is empty
146 QueuedMeshUpdate * MeshUpdateQueue::pop()
148 JMutexAutoLock lock(m_mutex);
150 bool must_be_urgent = !m_urgents.empty();
151 for(std::vector<QueuedMeshUpdate*>::iterator
153 i != m_queue.end(); i++)
155 QueuedMeshUpdate *q = *i;
156 if(must_be_urgent && m_urgents.count(q->p) == 0)
159 m_urgents.erase(q->p);
169 void * MeshUpdateThread::Thread()
173 log_register_thread("MeshUpdateThread");
175 DSTACK(__FUNCTION_NAME);
177 BEGIN_DEBUG_EXCEPTION_HANDLER
181 /*// Wait for output queue to flush.
182 // Allow 2 in queue, this makes less frametime jitter.
183 // Umm actually, there is no much difference
184 if(m_queue_out.size() >= 2)
190 QueuedMeshUpdate *q = m_queue_in.pop();
197 ScopeProfiler sp(g_profiler, "Client: Mesh making");
199 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
200 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
209 r.ack_block_to_server = q->ack_block_to_server;
211 /*infostream<<"MeshUpdateThread: Processed "
212 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
215 m_queue_out.push_back(r);
220 END_DEBUG_EXCEPTION_HANDLER(errorstream)
225 void * MediaFetchThread::Thread()
229 log_register_thread("MediaFetchThread");
231 DSTACK(__FUNCTION_NAME);
233 BEGIN_DEBUG_EXCEPTION_HANDLER
238 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
239 i != m_file_requests.end(); ++i) {
240 curl = curl_easy_init();
242 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
243 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
244 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
245 std::ostringstream stream;
246 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
247 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
248 res = curl_easy_perform(curl);
249 if (res == CURLE_OK) {
250 std::string data = stream.str();
251 m_file_data.push_back(make_pair(i->name, data));
253 m_failed.push_back(*i);
254 infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
256 curl_easy_cleanup(curl);
260 END_DEBUG_EXCEPTION_HANDLER(errorstream)
266 IrrlichtDevice *device,
267 const char *playername,
268 std::string password,
269 MapDrawControl &control,
270 IWritableTextureSource *tsrc,
271 IWritableShaderSource *shsrc,
272 IWritableItemDefManager *itemdef,
273 IWritableNodeDefManager *nodedef,
274 ISoundManager *sound,
275 MtEventManager *event,
284 m_mesh_update_thread(this),
286 new ClientMap(this, this, control,
287 device->getSceneManager()->getRootSceneNode(),
288 device->getSceneManager(), 666),
289 device->getSceneManager(),
292 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
294 m_server_ser_ver(SER_FMT_VER_INVALID),
296 m_inventory_updated(false),
297 m_inventory_from_server(NULL),
298 m_inventory_from_server_age(0.0),
303 m_password(password),
304 m_access_denied(false),
305 m_media_cache(getMediaCacheDir()),
306 m_media_receive_started(false),
308 m_media_received_count(0),
309 m_itemdef_received(false),
310 m_nodedef_received(false),
311 m_time_of_day_set(false),
312 m_last_time_of_day_f(-1),
313 m_time_of_day_update_timer(0),
314 m_recommended_send_interval(0.1),
315 m_removed_sounds_check_timer(0)
317 m_packetcounter_timer = 0.0;
318 //m_delete_unused_sectors_timer = 0.0;
319 m_connection_reinit_timer = 0.0;
320 m_avg_rtt_timer = 0.0;
321 m_playerpos_send_timer = 0.0;
322 m_ignore_damage_timer = 0.0;
328 Player *player = new LocalPlayer(this);
330 player->updateName(playername);
332 m_env.addPlayer(player);
335 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
336 m_media_fetch_threads.push_back(new MediaFetchThread(this));
342 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
346 m_mesh_update_thread.setRun(false);
347 while(m_mesh_update_thread.IsRunning())
349 while(!m_mesh_update_thread.m_queue_out.empty()) {
350 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
355 delete m_inventory_from_server;
357 // Delete detached inventories
359 for(std::map<std::string, Inventory*>::iterator
360 i = m_detached_inventories.begin();
361 i != m_detached_inventories.end(); i++){
366 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
367 i != m_media_fetch_threads.end(); ++i)
370 // cleanup 3d model meshes on client shutdown
371 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
372 scene::IAnimatedMesh * mesh =
373 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
376 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
380 void Client::connect(Address address)
382 DSTACK(__FUNCTION_NAME);
383 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
384 m_con.SetTimeoutMs(0);
385 m_con.Connect(address);
388 bool Client::connectedAndInitialized()
390 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
392 if(m_con.Connected() == false)
395 if(m_server_ser_ver == SER_FMT_VER_INVALID)
401 void Client::step(float dtime)
403 DSTACK(__FUNCTION_NAME);
409 if(m_ignore_damage_timer > dtime)
410 m_ignore_damage_timer -= dtime;
412 m_ignore_damage_timer = 0.0;
414 m_animation_time += dtime;
415 if(m_animation_time > 60.0)
416 m_animation_time -= 60.0;
418 m_time_of_day_update_timer += dtime;
420 //infostream<<"Client steps "<<dtime<<std::endl;
423 //TimeTaker timer("ReceiveAll()", m_device);
429 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
431 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
432 m_con.RunTimeouts(dtime);
439 float &counter = m_packetcounter_timer;
445 infostream<<"Client packetcounter (20s):"<<std::endl;
446 m_packetcounter.print(infostream);
447 m_packetcounter.clear();
451 // Get connection status
452 bool connected = connectedAndInitialized();
457 Delete unused sectors
459 NOTE: This jams the game for a while because deleting sectors
463 float &counter = m_delete_unused_sectors_timer;
471 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
473 core::list<v3s16> deleted_blocks;
475 float delete_unused_sectors_timeout =
476 g_settings->getFloat("client_delete_unused_sectors_timeout");
478 // Delete sector blocks
479 /*u32 num = m_env.getMap().unloadUnusedData
480 (delete_unused_sectors_timeout,
481 true, &deleted_blocks);*/
483 // Delete whole sectors
484 m_env.getMap().unloadUnusedData
485 (delete_unused_sectors_timeout,
488 if(deleted_blocks.size() > 0)
490 /*infostream<<"Client: Deleted blocks of "<<num
491 <<" unused sectors"<<std::endl;*/
492 /*infostream<<"Client: Deleted "<<num
493 <<" unused sectors"<<std::endl;*/
499 // Env is locked so con can be locked.
500 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
502 core::list<v3s16>::Iterator i = deleted_blocks.begin();
503 core::list<v3s16> sendlist;
506 if(sendlist.size() == 255 || i == deleted_blocks.end())
508 if(sendlist.size() == 0)
517 u32 replysize = 2+1+6*sendlist.size();
518 SharedBuffer<u8> reply(replysize);
519 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
520 reply[2] = sendlist.size();
522 for(core::list<v3s16>::Iterator
523 j = sendlist.begin();
524 j != sendlist.end(); j++)
526 writeV3S16(&reply[2+1+6*k], *j);
529 m_con.Send(PEER_ID_SERVER, 1, reply, true);
531 if(i == deleted_blocks.end())
537 sendlist.push_back(*i);
545 if(connected == false)
547 float &counter = m_connection_reinit_timer;
553 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
555 Player *myplayer = m_env.getLocalPlayer();
556 assert(myplayer != NULL);
558 // Send TOSERVER_INIT
559 // [0] u16 TOSERVER_INIT
560 // [2] u8 SER_FMT_VER_HIGHEST_READ
561 // [3] u8[20] player_name
562 // [23] u8[28] password (new in some version)
563 // [51] u16 minimum supported network protocol version (added sometime)
564 // [53] u16 maximum supported network protocol version (added later than the previous one)
565 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
566 writeU16(&data[0], TOSERVER_INIT);
567 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
569 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
570 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
572 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
575 memset((char*)&data[23], 0, PASSWORD_SIZE);
576 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
578 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
579 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
581 // Send as unreliable
582 Send(0, data, false);
585 // Not connected, return
590 Do stuff if connected
594 Run Map's timers and unload unused data
596 const float map_timer_and_unload_dtime = 5.25;
597 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
599 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
600 std::list<v3s16> deleted_blocks;
601 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
602 g_settings->getFloat("client_unload_unused_data_timeout"),
605 /*if(deleted_blocks.size() > 0)
606 infostream<<"Client: Unloaded "<<deleted_blocks.size()
607 <<" unused blocks"<<std::endl;*/
611 NOTE: This loop is intentionally iterated the way it is.
614 std::list<v3s16>::iterator i = deleted_blocks.begin();
615 std::list<v3s16> sendlist;
618 if(sendlist.size() == 255 || i == deleted_blocks.end())
620 if(sendlist.size() == 0)
629 u32 replysize = 2+1+6*sendlist.size();
630 SharedBuffer<u8> reply(replysize);
631 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
632 reply[2] = sendlist.size();
634 for(std::list<v3s16>::iterator
635 j = sendlist.begin();
636 j != sendlist.end(); ++j)
638 writeV3S16(&reply[2+1+6*k], *j);
641 m_con.Send(PEER_ID_SERVER, 1, reply, true);
643 if(i == deleted_blocks.end())
649 sendlist.push_back(*i);
659 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
661 // Control local player (0ms)
662 LocalPlayer *player = m_env.getLocalPlayer();
663 assert(player != NULL);
664 player->applyControl(dtime);
666 //TimeTaker envtimer("env step", m_device);
675 ClientEnvEvent event = m_env.getClientEvent();
676 if(event.type == CEE_NONE)
680 else if(event.type == CEE_PLAYER_DAMAGE)
682 if(m_ignore_damage_timer <= 0)
684 u8 damage = event.player_damage.amount;
686 if(event.player_damage.send_to_server)
689 // Add to ClientEvent queue
691 event.type = CE_PLAYER_DAMAGE;
692 event.player_damage.amount = damage;
693 m_client_event_queue.push_back(event);
696 else if(event.type == CEE_PLAYER_BREATH)
698 u16 breath = event.player_breath.amount;
708 float &counter = m_avg_rtt_timer;
713 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
714 // connectedAndInitialized() is true, peer exists.
715 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
716 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
721 Send player position to server
724 float &counter = m_playerpos_send_timer;
726 if(counter >= m_recommended_send_interval)
734 Replace updated meshes
737 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
739 //TimeTaker timer("** Processing mesh update result queue");
742 /*infostream<<"Mesh update result queue size is "
743 <<m_mesh_update_thread.m_queue_out.size()
746 int num_processed_meshes = 0;
747 while(!m_mesh_update_thread.m_queue_out.empty())
749 num_processed_meshes++;
750 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
751 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
754 //JMutexAutoLock lock(block->mesh_mutex);
756 // Delete the old mesh
757 if(block->mesh != NULL)
759 // TODO: Remove hardware buffers of meshbuffers of block->mesh
764 // Replace with the new mesh
765 block->mesh = r.mesh;
769 if(r.ack_block_to_server)
771 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
772 <<","<<r.p.Z<<")"<<std::endl;*/
783 u32 replysize = 2+1+6;
784 SharedBuffer<u8> reply(replysize);
785 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
787 writeV3S16(&reply[3], r.p);
789 m_con.Send(PEER_ID_SERVER, 1, reply, true);
792 if(num_processed_meshes > 0)
793 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
799 if (m_media_receive_started) {
800 bool all_stopped = true;
801 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
802 thread != m_media_fetch_threads.end(); ++thread) {
803 all_stopped &= !(*thread)->IsRunning();
804 while (!(*thread)->m_file_data.empty()) {
805 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
806 if(m_media_received_count < m_media_count)
807 m_media_received_count++;
809 bool success = loadMedia(out.second, out.first);
811 verbosestream<<"Client: Loaded received media: "
812 <<"\""<<out.first<<"\". Caching."<<std::endl;
814 infostream<<"Client: Failed to load received media: "
815 <<"\""<<out.first<<"\". Not caching."<<std::endl;
819 bool did = fs::CreateAllDirs(getMediaCacheDir());
821 errorstream<<"Could not create media cache directory"
826 std::map<std::string, std::string>::iterator n;
827 n = m_media_name_sha1_map.find(out.first);
828 if(n == m_media_name_sha1_map.end())
829 errorstream<<"The server sent a file that has not "
830 <<"been announced."<<std::endl;
832 m_media_cache.update_sha1(out.second);
837 std::list<MediaRequest> fetch_failed;
838 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
839 thread != m_media_fetch_threads.end(); ++thread) {
840 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
841 request != (*thread)->m_failed.end(); ++request)
842 fetch_failed.push_back(*request);
843 (*thread)->m_failed.clear();
845 if (fetch_failed.size() > 0) {
846 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
847 << "Requesting them the usual way." << std::endl;
848 request_media(fetch_failed);
854 If the server didn't update the inventory in a while, revert
855 the local inventory (so the player notices the lag problem
856 and knows something is wrong).
858 if(m_inventory_from_server)
860 float interval = 10.0;
861 float count_before = floor(m_inventory_from_server_age / interval);
863 m_inventory_from_server_age += dtime;
865 float count_after = floor(m_inventory_from_server_age / interval);
867 if(count_after != count_before)
869 // Do this every <interval> seconds after TOCLIENT_INVENTORY
870 // Reset the locally changed inventory to the authoritative inventory
871 Player *player = m_env.getLocalPlayer();
872 player->inventory = *m_inventory_from_server;
873 m_inventory_updated = true;
878 Update positions of sounds attached to objects
881 for(std::map<int, u16>::iterator
882 i = m_sounds_to_objects.begin();
883 i != m_sounds_to_objects.end(); i++)
885 int client_id = i->first;
886 u16 object_id = i->second;
887 ClientActiveObject *cao = m_env.getActiveObject(object_id);
890 v3f pos = cao->getPosition();
891 m_sound->updateSoundPosition(client_id, pos);
896 Handle removed remotely initiated sounds
898 m_removed_sounds_check_timer += dtime;
899 if(m_removed_sounds_check_timer >= 2.32)
901 m_removed_sounds_check_timer = 0;
902 // Find removed sounds and clear references to them
903 std::set<s32> removed_server_ids;
904 for(std::map<s32, int>::iterator
905 i = m_sounds_server_to_client.begin();
906 i != m_sounds_server_to_client.end();)
908 s32 server_id = i->first;
909 int client_id = i->second;
911 if(!m_sound->soundExists(client_id)){
912 m_sounds_server_to_client.erase(server_id);
913 m_sounds_client_to_server.erase(client_id);
914 m_sounds_to_objects.erase(client_id);
915 removed_server_ids.insert(server_id);
919 if(removed_server_ids.size() != 0)
921 std::ostringstream os(std::ios_base::binary);
922 writeU16(os, TOSERVER_REMOVED_SOUNDS);
923 writeU16(os, removed_server_ids.size());
924 for(std::set<s32>::iterator i = removed_server_ids.begin();
925 i != removed_server_ids.end(); i++)
927 std::string s = os.str();
928 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
935 bool Client::loadMedia(const std::string &data, const std::string &filename)
937 // Silly irrlicht's const-incorrectness
938 Buffer<char> data_rw(data.c_str(), data.size());
942 const char *image_ext[] = {
943 ".png", ".jpg", ".bmp", ".tga",
944 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
947 name = removeStringEnd(filename, image_ext);
950 verbosestream<<"Client: Attempting to load image "
951 <<"file \""<<filename<<"\""<<std::endl;
953 io::IFileSystem *irrfs = m_device->getFileSystem();
954 video::IVideoDriver *vdrv = m_device->getVideoDriver();
956 // Create an irrlicht memory file
957 io::IReadFile *rfile = irrfs->createMemoryReadFile(
958 *data_rw, data_rw.getSize(), "_tempreadfile");
961 video::IImage *img = vdrv->createImageFromFile(rfile);
963 errorstream<<"Client: Cannot create image from data of "
964 <<"file \""<<filename<<"\""<<std::endl;
969 m_tsrc->insertSourceImage(filename, img);
976 const char *sound_ext[] = {
977 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
978 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
981 name = removeStringEnd(filename, sound_ext);
984 verbosestream<<"Client: Attempting to load sound "
985 <<"file \""<<filename<<"\""<<std::endl;
986 m_sound->loadSoundData(name, data);
990 const char *model_ext[] = {
991 ".x", ".b3d", ".md2", ".obj",
994 name = removeStringEnd(filename, model_ext);
997 verbosestream<<"Client: Storing model into Irrlicht: "
998 <<"\""<<filename<<"\""<<std::endl;
999 scene::ISceneManager *smgr = m_device->getSceneManager();
1001 //check if mesh was already cached
1002 scene::IAnimatedMesh *mesh =
1003 smgr->getMeshCache()->getMeshByName(filename.c_str());
1006 errorstream << "Multiple models with name: " << filename.c_str() <<
1007 " found replacing previous model!" << std::endl;
1009 smgr->getMeshCache()->removeMesh(mesh);
1013 io::IFileSystem *irrfs = m_device->getFileSystem();
1014 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1015 *data_rw, data_rw.getSize(), filename.c_str());
1018 mesh = smgr->getMesh(rfile);
1019 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1024 errorstream<<"Client: Don't know how to load file \""
1025 <<filename<<"\""<<std::endl;
1029 // Virtual methods from con::PeerHandler
1030 void Client::peerAdded(con::Peer *peer)
1032 infostream<<"Client::peerAdded(): peer->id="
1033 <<peer->id<<std::endl;
1035 void Client::deletingPeer(con::Peer *peer, bool timeout)
1037 infostream<<"Client::deletingPeer(): "
1038 "Server Peer is getting deleted "
1039 <<"(timeout="<<timeout<<")"<<std::endl;
1044 u16 number of files requested
1050 void Client::request_media(const std::list<MediaRequest> &file_requests)
1052 std::ostringstream os(std::ios_base::binary);
1053 writeU16(os, TOSERVER_REQUEST_MEDIA);
1054 writeU16(os, file_requests.size());
1056 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1057 i != file_requests.end(); ++i) {
1058 os<<serializeString(i->name);
1062 std::string s = os.str();
1063 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1065 Send(0, data, true);
1066 infostream<<"Client: Sending media request list to server ("
1067 <<file_requests.size()<<" files)"<<std::endl;
1070 void Client::ReceiveAll()
1072 DSTACK(__FUNCTION_NAME);
1073 u32 start_ms = porting::getTimeMs();
1076 // Limit time even if there would be huge amounts of data to
1078 if(porting::getTimeMs() > start_ms + 100)
1083 g_profiler->graphAdd("client_received_packets", 1);
1085 catch(con::NoIncomingDataException &e)
1089 catch(con::InvalidIncomingDataException &e)
1091 infostream<<"Client::ReceiveAll(): "
1092 "InvalidIncomingDataException: what()="
1093 <<e.what()<<std::endl;
1098 void Client::Receive()
1100 DSTACK(__FUNCTION_NAME);
1101 SharedBuffer<u8> data;
1105 //TimeTaker t1("con mutex and receive", m_device);
1106 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1107 datasize = m_con.Receive(sender_peer_id, data);
1109 //TimeTaker t1("ProcessData", m_device);
1110 ProcessData(*data, datasize, sender_peer_id);
1114 sender_peer_id given to this shall be quaranteed to be a valid peer
1116 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1118 DSTACK(__FUNCTION_NAME);
1120 // Ignore packets that don't even fit a command
1123 m_packetcounter.add(60000);
1127 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1129 //infostream<<"Client: received command="<<command<<std::endl;
1130 m_packetcounter.add((u16)command);
1133 If this check is removed, be sure to change the queue
1134 system to know the ids
1136 if(sender_peer_id != PEER_ID_SERVER)
1138 infostream<<"Client::ProcessData(): Discarding data not "
1139 "coming from server: peer_id="<<sender_peer_id
1144 u8 ser_version = m_server_ser_ver;
1146 //infostream<<"Client received command="<<(int)command<<std::endl;
1148 if(command == TOCLIENT_INIT)
1153 u8 deployed = data[2];
1155 infostream<<"Client: TOCLIENT_INIT received with "
1156 "deployed="<<((int)deployed&0xff)<<std::endl;
1158 if(!ser_ver_supported(deployed))
1160 infostream<<"Client: TOCLIENT_INIT: Server sent "
1161 <<"unsupported ser_fmt_ver"<<std::endl;
1165 m_server_ser_ver = deployed;
1167 // Get player position
1168 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1169 if(datasize >= 2+1+6)
1170 playerpos_s16 = readV3S16(&data[2+1]);
1171 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1174 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1176 // Set player position
1177 Player *player = m_env.getLocalPlayer();
1178 assert(player != NULL);
1179 player->setPosition(playerpos_f);
1182 if(datasize >= 2+1+6+8)
1185 m_map_seed = readU64(&data[2+1+6]);
1186 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1189 if(datasize >= 2+1+6+8+4)
1192 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1193 infostream<<"Client: received recommended send interval "
1194 <<m_recommended_send_interval<<std::endl;
1199 SharedBuffer<u8> reply(replysize);
1200 writeU16(&reply[0], TOSERVER_INIT2);
1202 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1207 if(command == TOCLIENT_ACCESS_DENIED)
1209 // The server didn't like our password. Note, this needs
1210 // to be processed even if the serialisation format has
1211 // not been agreed yet, the same as TOCLIENT_INIT.
1212 m_access_denied = true;
1213 m_access_denied_reason = L"Unknown";
1216 std::string datastring((char*)&data[2], datasize-2);
1217 std::istringstream is(datastring, std::ios_base::binary);
1218 m_access_denied_reason = deSerializeWideString(is);
1223 if(ser_version == SER_FMT_VER_INVALID)
1225 infostream<<"Client: Server serialization"
1226 " format invalid or not initialized."
1227 " Skipping incoming command="<<command<<std::endl;
1231 // Just here to avoid putting the two if's together when
1232 // making some copypasta
1235 if(command == TOCLIENT_REMOVENODE)
1240 p.X = readS16(&data[2]);
1241 p.Y = readS16(&data[4]);
1242 p.Z = readS16(&data[6]);
1244 //TimeTaker t1("TOCLIENT_REMOVENODE");
1248 else if(command == TOCLIENT_ADDNODE)
1250 if(datasize < 8 + MapNode::serializedLength(ser_version))
1254 p.X = readS16(&data[2]);
1255 p.Y = readS16(&data[4]);
1256 p.Z = readS16(&data[6]);
1258 //TimeTaker t1("TOCLIENT_ADDNODE");
1261 n.deSerialize(&data[8], ser_version);
1265 else if(command == TOCLIENT_BLOCKDATA)
1267 // Ignore too small packet
1272 p.X = readS16(&data[2]);
1273 p.Y = readS16(&data[4]);
1274 p.Z = readS16(&data[6]);
1276 /*infostream<<"Client: Thread: BLOCKDATA for ("
1277 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1278 /*infostream<<"Client: Thread: BLOCKDATA for ("
1279 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1281 std::string datastring((char*)&data[8], datasize-8);
1282 std::istringstream istr(datastring, std::ios_base::binary);
1287 v2s16 p2d(p.X, p.Z);
1288 sector = m_env.getMap().emergeSector(p2d);
1290 assert(sector->getPos() == p2d);
1292 //TimeTaker timer("MapBlock deSerialize");
1295 block = sector->getBlockNoCreateNoEx(p.Y);
1299 Update an existing block
1301 //infostream<<"Updating"<<std::endl;
1302 block->deSerialize(istr, ser_version, false);
1303 block->deSerializeNetworkSpecific(istr);
1310 //infostream<<"Creating new"<<std::endl;
1311 block = new MapBlock(&m_env.getMap(), p, this);
1312 block->deSerialize(istr, ser_version, false);
1313 block->deSerializeNetworkSpecific(istr);
1314 sector->insertBlock(block);
1328 u32 replysize = 2+1+6;
1329 SharedBuffer<u8> reply(replysize);
1330 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1332 writeV3S16(&reply[3], p);
1334 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1338 Add it to mesh update queue and set it to be acknowledged after update.
1340 //infostream<<"Adding mesh update task for received block"<<std::endl;
1341 addUpdateMeshTaskWithEdge(p, true);
1343 else if(command == TOCLIENT_INVENTORY)
1348 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1351 //TimeTaker t2("mutex locking", m_device);
1352 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1355 //TimeTaker t3("istringstream init", m_device);
1356 std::string datastring((char*)&data[2], datasize-2);
1357 std::istringstream is(datastring, std::ios_base::binary);
1360 //TimeTaker t4("player get", m_device);
1361 Player *player = m_env.getLocalPlayer();
1362 assert(player != NULL);
1365 //TimeTaker t1("inventory.deSerialize()", m_device);
1366 player->inventory.deSerialize(is);
1369 m_inventory_updated = true;
1371 delete m_inventory_from_server;
1372 m_inventory_from_server = new Inventory(player->inventory);
1373 m_inventory_from_server_age = 0.0;
1375 //infostream<<"Client got player inventory:"<<std::endl;
1376 //player->inventory.print(infostream);
1379 else if(command == TOCLIENT_TIME_OF_DAY)
1384 u16 time_of_day = readU16(&data[2]);
1385 time_of_day = time_of_day % 24000;
1386 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1387 float time_speed = 0;
1388 if(datasize >= 2 + 2 + 4){
1389 time_speed = readF1000(&data[4]);
1391 // Old message; try to approximate speed of time by ourselves
1392 float time_of_day_f = (float)time_of_day / 24000.0;
1393 float tod_diff_f = 0;
1394 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1395 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1397 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1398 m_last_time_of_day_f = time_of_day_f;
1399 float time_diff = m_time_of_day_update_timer;
1400 m_time_of_day_update_timer = 0;
1401 if(m_time_of_day_set){
1402 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1403 infostream<<"Client: Measured time_of_day speed (old format): "
1404 <<time_speed<<" tod_diff_f="<<tod_diff_f
1405 <<" time_diff="<<time_diff<<std::endl;
1409 // Update environment
1410 m_env.setTimeOfDay(time_of_day);
1411 m_env.setTimeOfDaySpeed(time_speed);
1412 m_time_of_day_set = true;
1414 u32 dr = m_env.getDayNightRatio();
1415 verbosestream<<"Client: time_of_day="<<time_of_day
1416 <<" time_speed="<<time_speed
1417 <<" dr="<<dr<<std::endl;
1419 else if(command == TOCLIENT_CHAT_MESSAGE)
1427 std::string datastring((char*)&data[2], datasize-2);
1428 std::istringstream is(datastring, std::ios_base::binary);
1431 is.read((char*)buf, 2);
1432 u16 len = readU16(buf);
1434 std::wstring message;
1435 for(u16 i=0; i<len; i++)
1437 is.read((char*)buf, 2);
1438 message += (wchar_t)readU16(buf);
1441 /*infostream<<"Client received chat message: "
1442 <<wide_to_narrow(message)<<std::endl;*/
1444 m_chat_queue.push_back(message);
1446 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1448 //if(g_settings->getBool("enable_experimental"))
1452 u16 count of removed objects
1453 for all removed objects {
1456 u16 count of added objects
1457 for all added objects {
1460 u32 initialization data length
1461 string initialization data
1466 // Get all data except the command number
1467 std::string datastring((char*)&data[2], datasize-2);
1468 // Throw them in an istringstream
1469 std::istringstream is(datastring, std::ios_base::binary);
1473 // Read removed objects
1475 u16 removed_count = readU16((u8*)buf);
1476 for(u16 i=0; i<removed_count; i++)
1479 u16 id = readU16((u8*)buf);
1482 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1483 m_env.removeActiveObject(id);
1487 // Read added objects
1489 u16 added_count = readU16((u8*)buf);
1490 for(u16 i=0; i<added_count; i++)
1493 u16 id = readU16((u8*)buf);
1495 u8 type = readU8((u8*)buf);
1496 std::string data = deSerializeLongString(is);
1499 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1500 m_env.addActiveObject(id, type, data);
1505 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1507 //if(g_settings->getBool("enable_experimental"))
1519 // Get all data except the command number
1520 std::string datastring((char*)&data[2], datasize-2);
1521 // Throw them in an istringstream
1522 std::istringstream is(datastring, std::ios_base::binary);
1524 while(is.eof() == false)
1528 u16 id = readU16((u8*)buf);
1532 u16 message_size = readU16((u8*)buf);
1533 std::string message;
1534 message.reserve(message_size);
1535 for(u16 i=0; i<message_size; i++)
1538 message.append(buf, 1);
1540 // Pass on to the environment
1542 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1543 m_env.processActiveObjectMessage(id, message);
1548 else if(command == TOCLIENT_MOVEMENT)
1550 std::string datastring((char*)&data[2], datasize-2);
1551 std::istringstream is(datastring, std::ios_base::binary);
1552 Player *player = m_env.getLocalPlayer();
1553 assert(player != NULL);
1555 player->movement_acceleration_default = readF1000(is) * BS;
1556 player->movement_acceleration_air = readF1000(is) * BS;
1557 player->movement_acceleration_fast = readF1000(is) * BS;
1558 player->movement_speed_walk = readF1000(is) * BS;
1559 player->movement_speed_crouch = readF1000(is) * BS;
1560 player->movement_speed_fast = readF1000(is) * BS;
1561 player->movement_speed_climb = readF1000(is) * BS;
1562 player->movement_speed_jump = readF1000(is) * BS;
1563 player->movement_liquid_fluidity = readF1000(is) * BS;
1564 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1565 player->movement_liquid_sink = readF1000(is) * BS;
1566 player->movement_gravity = readF1000(is) * BS;
1568 else if(command == TOCLIENT_HP)
1570 std::string datastring((char*)&data[2], datasize-2);
1571 std::istringstream is(datastring, std::ios_base::binary);
1572 Player *player = m_env.getLocalPlayer();
1573 assert(player != NULL);
1574 u8 oldhp = player->hp;
1580 // Add to ClientEvent queue
1582 event.type = CE_PLAYER_DAMAGE;
1583 event.player_damage.amount = oldhp - hp;
1584 m_client_event_queue.push_back(event);
1587 else if(command == TOCLIENT_BREATH)
1589 std::string datastring((char*)&data[2], datasize-2);
1590 std::istringstream is(datastring, std::ios_base::binary);
1591 Player *player = m_env.getLocalPlayer();
1592 assert(player != NULL);
1593 u16 breath = readU16(is);
1594 player->setBreath(breath) ;
1596 else if(command == TOCLIENT_MOVE_PLAYER)
1598 std::string datastring((char*)&data[2], datasize-2);
1599 std::istringstream is(datastring, std::ios_base::binary);
1600 Player *player = m_env.getLocalPlayer();
1601 assert(player != NULL);
1602 v3f pos = readV3F1000(is);
1603 f32 pitch = readF1000(is);
1604 f32 yaw = readF1000(is);
1605 player->setPosition(pos);
1606 /*player->setPitch(pitch);
1607 player->setYaw(yaw);*/
1609 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1610 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1616 Add to ClientEvent queue.
1617 This has to be sent to the main program because otherwise
1618 it would just force the pitch and yaw values to whatever
1619 the camera points to.
1622 event.type = CE_PLAYER_FORCE_MOVE;
1623 event.player_force_move.pitch = pitch;
1624 event.player_force_move.yaw = yaw;
1625 m_client_event_queue.push_back(event);
1627 // Ignore damage for a few seconds, so that the player doesn't
1628 // get damage from falling on ground
1629 m_ignore_damage_timer = 3.0;
1631 else if(command == TOCLIENT_PLAYERITEM)
1633 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1635 else if(command == TOCLIENT_DEATHSCREEN)
1637 std::string datastring((char*)&data[2], datasize-2);
1638 std::istringstream is(datastring, std::ios_base::binary);
1640 bool set_camera_point_target = readU8(is);
1641 v3f camera_point_target = readV3F1000(is);
1644 event.type = CE_DEATHSCREEN;
1645 event.deathscreen.set_camera_point_target = set_camera_point_target;
1646 event.deathscreen.camera_point_target_x = camera_point_target.X;
1647 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1648 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1649 m_client_event_queue.push_back(event);
1651 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1653 std::string datastring((char*)&data[2], datasize-2);
1654 std::istringstream is(datastring, std::ios_base::binary);
1656 // Mesh update thread must be stopped while
1657 // updating content definitions
1658 assert(!m_mesh_update_thread.IsRunning());
1660 int num_files = readU16(is);
1662 infostream<<"Client: Received media announcement: packet size: "
1663 <<datasize<<std::endl;
1665 std::list<MediaRequest> file_requests;
1667 for(int i=0; i<num_files; i++)
1669 //read file from cache
1670 std::string name = deSerializeString(is);
1671 std::string sha1_base64 = deSerializeString(is);
1673 // if name contains illegal characters, ignore the file
1674 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1675 errorstream<<"Client: ignoring illegal file name "
1676 <<"sent by server: \""<<name<<"\""<<std::endl;
1680 std::string sha1_raw = base64_decode(sha1_base64);
1681 std::string sha1_hex = hex_encode(sha1_raw);
1682 std::ostringstream tmp_os(std::ios_base::binary);
1683 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1684 m_media_name_sha1_map[name] = sha1_raw;
1686 // If found in cache, try to load it from there
1689 bool success = loadMedia(tmp_os.str(), name);
1691 verbosestream<<"Client: Loaded cached media: "
1692 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1695 infostream<<"Client: Failed to load cached media: "
1696 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1699 // Didn't load from cache; queue it to be requested
1700 verbosestream<<"Client: Adding file to request list: \""
1701 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1702 file_requests.push_back(MediaRequest(name));
1705 std::string remote_media = "";
1707 remote_media = deSerializeString(is);
1709 catch(SerializationError) {
1710 // not supported by server or turned off
1713 m_media_count = file_requests.size();
1714 m_media_receive_started = true;
1716 if (remote_media == "" || !USE_CURL) {
1717 request_media(file_requests);
1720 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1721 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1722 i != file_requests.end(); ++i) {
1723 (*cur)->m_file_requests.push_back(*i);
1725 if (cur == m_media_fetch_threads.end())
1726 cur = m_media_fetch_threads.begin();
1728 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1729 i != m_media_fetch_threads.end(); ++i) {
1730 (*i)->m_remote_url = remote_media;
1735 // notify server we received everything
1736 std::ostringstream os(std::ios_base::binary);
1737 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1738 std::string s = os.str();
1739 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1741 Send(0, data, true);
1744 event.type = CE_TEXTURES_UPDATED;
1745 m_client_event_queue.push_back(event);
1747 else if(command == TOCLIENT_MEDIA)
1749 std::string datastring((char*)&data[2], datasize-2);
1750 std::istringstream is(datastring, std::ios_base::binary);
1754 u16 total number of file bunches
1755 u16 index of this bunch
1756 u32 number of files in this bunch
1764 int num_bunches = readU16(is);
1765 int bunch_i = readU16(is);
1766 u32 num_files = readU32(is);
1767 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1768 <<num_bunches<<" files="<<num_files
1769 <<" size="<<datasize<<std::endl;
1771 // Check total and received media count
1772 assert(m_media_received_count <= m_media_count);
1773 if (num_files > m_media_count - m_media_received_count) {
1774 errorstream<<"Client: Received more files than requested:"
1775 <<" total count="<<m_media_count
1776 <<" total received="<<m_media_received_count
1777 <<" bunch "<<bunch_i<<"/"<<num_bunches
1778 <<" files="<<num_files
1779 <<" size="<<datasize<<std::endl;
1780 num_files = m_media_count - m_media_received_count;
1785 // Mesh update thread must be stopped while
1786 // updating content definitions
1787 assert(!m_mesh_update_thread.IsRunning());
1789 for(u32 i=0; i<num_files; i++){
1790 assert(m_media_received_count < m_media_count);
1791 m_media_received_count++;
1792 std::string name = deSerializeString(is);
1793 std::string data = deSerializeLongString(is);
1795 // if name contains illegal characters, ignore the file
1796 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1797 errorstream<<"Client: ignoring illegal file name "
1798 <<"sent by server: \""<<name<<"\""<<std::endl;
1802 bool success = loadMedia(data, name);
1804 verbosestream<<"Client: Loaded received media: "
1805 <<"\""<<name<<"\". Caching."<<std::endl;
1807 infostream<<"Client: Failed to load received media: "
1808 <<"\""<<name<<"\". Not caching."<<std::endl;
1812 bool did = fs::CreateAllDirs(getMediaCacheDir());
1814 errorstream<<"Could not create media cache directory"
1819 std::map<std::string, std::string>::iterator n;
1820 n = m_media_name_sha1_map.find(name);
1821 if(n == m_media_name_sha1_map.end())
1822 errorstream<<"The server sent a file that has not "
1823 <<"been announced."<<std::endl;
1825 m_media_cache.update_sha1(data);
1830 event.type = CE_TEXTURES_UPDATED;
1831 m_client_event_queue.push_back(event);
1833 else if(command == TOCLIENT_TOOLDEF)
1835 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1837 else if(command == TOCLIENT_NODEDEF)
1839 infostream<<"Client: Received node definitions: packet size: "
1840 <<datasize<<std::endl;
1842 // Mesh update thread must be stopped while
1843 // updating content definitions
1844 assert(!m_mesh_update_thread.IsRunning());
1846 // Decompress node definitions
1847 std::string datastring((char*)&data[2], datasize-2);
1848 std::istringstream is(datastring, std::ios_base::binary);
1849 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1850 std::ostringstream tmp_os;
1851 decompressZlib(tmp_is, tmp_os);
1853 // Deserialize node definitions
1854 std::istringstream tmp_is2(tmp_os.str());
1855 m_nodedef->deSerialize(tmp_is2);
1856 m_nodedef_received = true;
1858 else if(command == TOCLIENT_CRAFTITEMDEF)
1860 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1862 else if(command == TOCLIENT_ITEMDEF)
1864 infostream<<"Client: Received item definitions: packet size: "
1865 <<datasize<<std::endl;
1867 // Mesh update thread must be stopped while
1868 // updating content definitions
1869 assert(!m_mesh_update_thread.IsRunning());
1871 // Decompress item definitions
1872 std::string datastring((char*)&data[2], datasize-2);
1873 std::istringstream is(datastring, std::ios_base::binary);
1874 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1875 std::ostringstream tmp_os;
1876 decompressZlib(tmp_is, tmp_os);
1878 // Deserialize node definitions
1879 std::istringstream tmp_is2(tmp_os.str());
1880 m_itemdef->deSerialize(tmp_is2);
1881 m_itemdef_received = true;
1883 else if(command == TOCLIENT_PLAY_SOUND)
1885 std::string datastring((char*)&data[2], datasize-2);
1886 std::istringstream is(datastring, std::ios_base::binary);
1888 s32 server_id = readS32(is);
1889 std::string name = deSerializeString(is);
1890 float gain = readF1000(is);
1891 int type = readU8(is); // 0=local, 1=positional, 2=object
1892 v3f pos = readV3F1000(is);
1893 u16 object_id = readU16(is);
1894 bool loop = readU8(is);
1899 client_id = m_sound->playSound(name, loop, gain);
1901 case 1: // positional
1902 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1905 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1907 pos = cao->getPosition();
1908 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1909 // TODO: Set up sound to move with object
1914 if(client_id != -1){
1915 m_sounds_server_to_client[server_id] = client_id;
1916 m_sounds_client_to_server[client_id] = server_id;
1918 m_sounds_to_objects[client_id] = object_id;
1921 else if(command == TOCLIENT_STOP_SOUND)
1923 std::string datastring((char*)&data[2], datasize-2);
1924 std::istringstream is(datastring, std::ios_base::binary);
1926 s32 server_id = readS32(is);
1927 std::map<s32, int>::iterator i =
1928 m_sounds_server_to_client.find(server_id);
1929 if(i != m_sounds_server_to_client.end()){
1930 int client_id = i->second;
1931 m_sound->stopSound(client_id);
1934 else if(command == TOCLIENT_PRIVILEGES)
1936 std::string datastring((char*)&data[2], datasize-2);
1937 std::istringstream is(datastring, std::ios_base::binary);
1939 m_privileges.clear();
1940 infostream<<"Client: Privileges updated: ";
1941 u16 num_privileges = readU16(is);
1942 for(u16 i=0; i<num_privileges; i++){
1943 std::string priv = deSerializeString(is);
1944 m_privileges.insert(priv);
1945 infostream<<priv<<" ";
1947 infostream<<std::endl;
1949 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1951 std::string datastring((char*)&data[2], datasize-2);
1952 std::istringstream is(datastring, std::ios_base::binary);
1954 // Store formspec in LocalPlayer
1955 Player *player = m_env.getLocalPlayer();
1956 assert(player != NULL);
1957 player->inventory_formspec = deSerializeLongString(is);
1959 else if(command == TOCLIENT_DETACHED_INVENTORY)
1961 std::string datastring((char*)&data[2], datasize-2);
1962 std::istringstream is(datastring, std::ios_base::binary);
1964 std::string name = deSerializeString(is);
1966 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1968 Inventory *inv = NULL;
1969 if(m_detached_inventories.count(name) > 0)
1970 inv = m_detached_inventories[name];
1972 inv = new Inventory(m_itemdef);
1973 m_detached_inventories[name] = inv;
1975 inv->deSerialize(is);
1977 else if(command == TOCLIENT_SHOW_FORMSPEC)
1979 std::string datastring((char*)&data[2], datasize-2);
1980 std::istringstream is(datastring, std::ios_base::binary);
1982 std::string formspec = deSerializeLongString(is);
1983 std::string formname = deSerializeString(is);
1986 event.type = CE_SHOW_FORMSPEC;
1987 // pointer is required as event is a struct only!
1988 // adding a std:string to a struct isn't possible
1989 event.show_formspec.formspec = new std::string(formspec);
1990 event.show_formspec.formname = new std::string(formname);
1991 m_client_event_queue.push_back(event);
1993 else if(command == TOCLIENT_SPAWN_PARTICLE)
1995 std::string datastring((char*)&data[2], datasize-2);
1996 std::istringstream is(datastring, std::ios_base::binary);
1998 v3f pos = readV3F1000(is);
1999 v3f vel = readV3F1000(is);
2000 v3f acc = readV3F1000(is);
2001 float expirationtime = readF1000(is);
2002 float size = readF1000(is);
2003 bool collisiondetection = readU8(is);
2004 std::string texture = deSerializeLongString(is);
2007 event.type = CE_SPAWN_PARTICLE;
2008 event.spawn_particle.pos = new v3f (pos);
2009 event.spawn_particle.vel = new v3f (vel);
2010 event.spawn_particle.acc = new v3f (acc);
2012 event.spawn_particle.expirationtime = expirationtime;
2013 event.spawn_particle.size = size;
2014 event.spawn_particle.collisiondetection =
2016 event.spawn_particle.texture = new std::string(texture);
2018 m_client_event_queue.push_back(event);
2020 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2022 std::string datastring((char*)&data[2], datasize-2);
2023 std::istringstream is(datastring, std::ios_base::binary);
2025 u16 amount = readU16(is);
2026 float spawntime = readF1000(is);
2027 v3f minpos = readV3F1000(is);
2028 v3f maxpos = readV3F1000(is);
2029 v3f minvel = readV3F1000(is);
2030 v3f maxvel = readV3F1000(is);
2031 v3f minacc = readV3F1000(is);
2032 v3f maxacc = readV3F1000(is);
2033 float minexptime = readF1000(is);
2034 float maxexptime = readF1000(is);
2035 float minsize = readF1000(is);
2036 float maxsize = readF1000(is);
2037 bool collisiondetection = readU8(is);
2038 std::string texture = deSerializeLongString(is);
2039 u32 id = readU32(is);
2042 event.type = CE_ADD_PARTICLESPAWNER;
2043 event.add_particlespawner.amount = amount;
2044 event.add_particlespawner.spawntime = spawntime;
2046 event.add_particlespawner.minpos = new v3f (minpos);
2047 event.add_particlespawner.maxpos = new v3f (maxpos);
2048 event.add_particlespawner.minvel = new v3f (minvel);
2049 event.add_particlespawner.maxvel = new v3f (maxvel);
2050 event.add_particlespawner.minacc = new v3f (minacc);
2051 event.add_particlespawner.maxacc = new v3f (maxacc);
2053 event.add_particlespawner.minexptime = minexptime;
2054 event.add_particlespawner.maxexptime = maxexptime;
2055 event.add_particlespawner.minsize = minsize;
2056 event.add_particlespawner.maxsize = maxsize;
2057 event.add_particlespawner.collisiondetection = collisiondetection;
2058 event.add_particlespawner.texture = new std::string(texture);
2059 event.add_particlespawner.id = id;
2061 m_client_event_queue.push_back(event);
2063 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2065 std::string datastring((char*)&data[2], datasize-2);
2066 std::istringstream is(datastring, std::ios_base::binary);
2068 u32 id = readU16(is);
2071 event.type = CE_DELETE_PARTICLESPAWNER;
2072 event.delete_particlespawner.id = id;
2074 m_client_event_queue.push_back(event);
2076 else if(command == TOCLIENT_HUDADD)
2078 std::string datastring((char *)&data[2], datasize - 2);
2079 std::istringstream is(datastring, std::ios_base::binary);
2081 u32 id = readU32(is);
2082 u8 type = readU8(is);
2083 v2f pos = readV2F1000(is);
2084 std::string name = deSerializeString(is);
2085 v2f scale = readV2F1000(is);
2086 std::string text = deSerializeString(is);
2087 u32 number = readU32(is);
2088 u32 item = readU32(is);
2089 u32 dir = readU32(is);
2090 v2f align = readV2F1000(is);
2091 v2f offset = readV2F1000(is);
2094 event.type = CE_HUDADD;
2095 event.hudadd.id = id;
2096 event.hudadd.type = type;
2097 event.hudadd.pos = new v2f(pos);
2098 event.hudadd.name = new std::string(name);
2099 event.hudadd.scale = new v2f(scale);
2100 event.hudadd.text = new std::string(text);
2101 event.hudadd.number = number;
2102 event.hudadd.item = item;
2103 event.hudadd.dir = dir;
2104 event.hudadd.align = new v2f(align);
2105 event.hudadd.offset = new v2f(offset);
2106 m_client_event_queue.push_back(event);
2108 else if(command == TOCLIENT_HUDRM)
2110 std::string datastring((char *)&data[2], datasize - 2);
2111 std::istringstream is(datastring, std::ios_base::binary);
2113 u32 id = readU32(is);
2116 event.type = CE_HUDRM;
2117 event.hudrm.id = id;
2118 m_client_event_queue.push_back(event);
2120 else if(command == TOCLIENT_HUDCHANGE)
2126 std::string datastring((char *)&data[2], datasize - 2);
2127 std::istringstream is(datastring, std::ios_base::binary);
2129 u32 id = readU32(is);
2130 u8 stat = (HudElementStat)readU8(is);
2132 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2133 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2134 v2fdata = readV2F1000(is);
2135 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2136 sdata = deSerializeString(is);
2138 intdata = readU32(is);
2141 event.type = CE_HUDCHANGE;
2142 event.hudchange.id = id;
2143 event.hudchange.stat = (HudElementStat)stat;
2144 event.hudchange.v2fdata = new v2f(v2fdata);
2145 event.hudchange.sdata = new std::string(sdata);
2146 event.hudchange.data = intdata;
2147 m_client_event_queue.push_back(event);
2149 else if(command == TOCLIENT_HUD_SET_FLAGS)
2151 std::string datastring((char *)&data[2], datasize - 2);
2152 std::istringstream is(datastring, std::ios_base::binary);
2154 Player *player = m_env.getLocalPlayer();
2155 assert(player != NULL);
2157 u32 flags = readU32(is);
2158 u32 mask = readU32(is);
2160 player->hud_flags &= ~mask;
2161 player->hud_flags |= flags;
2163 else if(command == TOCLIENT_HUD_SET_PARAM)
2165 std::string datastring((char *)&data[2], datasize - 2);
2166 std::istringstream is(datastring, std::ios_base::binary);
2168 Player *player = m_env.getLocalPlayer();
2169 assert(player != NULL);
2171 u16 param = readU16(is);
2172 std::string value = deSerializeString(is);
2174 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2175 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2176 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2177 player->hud_hotbar_itemcount = hotbar_itemcount;
2178 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2179 ((LocalPlayer *) player)->hotbar_image = value;
2180 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2181 ((LocalPlayer *) player)->hotbar_selected_image = value;
2186 infostream<<"Client: Ignoring unknown command "
2187 <<command<<std::endl;
2191 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2193 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2194 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2197 void Client::interact(u8 action, const PointedThing& pointed)
2199 if(connectedAndInitialized() == false){
2200 infostream<<"Client::interact() "
2201 "cancelled (not connected)"
2206 std::ostringstream os(std::ios_base::binary);
2212 [5] u32 length of the next item
2213 [9] serialized PointedThing
2215 0: start digging (from undersurface) or use
2216 1: stop digging (all parameters ignored)
2217 2: digging completed
2218 3: place block or item (to abovesurface)
2221 writeU16(os, TOSERVER_INTERACT);
2222 writeU8(os, action);
2223 writeU16(os, getPlayerItem());
2224 std::ostringstream tmp_os(std::ios::binary);
2225 pointed.serialize(tmp_os);
2226 os<<serializeLongString(tmp_os.str());
2228 std::string s = os.str();
2229 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2232 Send(0, data, true);
2235 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2236 const std::map<std::string, std::string> &fields)
2238 std::ostringstream os(std::ios_base::binary);
2240 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2242 os<<serializeString(formname);
2243 writeU16(os, fields.size());
2244 for(std::map<std::string, std::string>::const_iterator
2245 i = fields.begin(); i != fields.end(); i++){
2246 const std::string &name = i->first;
2247 const std::string &value = i->second;
2248 os<<serializeString(name);
2249 os<<serializeLongString(value);
2253 std::string s = os.str();
2254 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2256 Send(0, data, true);
2259 void Client::sendInventoryFields(const std::string &formname,
2260 const std::map<std::string, std::string> &fields)
2262 std::ostringstream os(std::ios_base::binary);
2264 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2265 os<<serializeString(formname);
2266 writeU16(os, fields.size());
2267 for(std::map<std::string, std::string>::const_iterator
2268 i = fields.begin(); i != fields.end(); i++){
2269 const std::string &name = i->first;
2270 const std::string &value = i->second;
2271 os<<serializeString(name);
2272 os<<serializeLongString(value);
2276 std::string s = os.str();
2277 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2279 Send(0, data, true);
2282 void Client::sendInventoryAction(InventoryAction *a)
2284 std::ostringstream os(std::ios_base::binary);
2288 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2289 os.write((char*)buf, 2);
2294 std::string s = os.str();
2295 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2297 Send(0, data, true);
2300 void Client::sendChatMessage(const std::wstring &message)
2302 std::ostringstream os(std::ios_base::binary);
2306 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2307 os.write((char*)buf, 2);
2310 writeU16(buf, message.size());
2311 os.write((char*)buf, 2);
2314 for(u32 i=0; i<message.size(); i++)
2318 os.write((char*)buf, 2);
2322 std::string s = os.str();
2323 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2325 Send(0, data, true);
2328 void Client::sendChangePassword(const std::wstring oldpassword,
2329 const std::wstring newpassword)
2331 Player *player = m_env.getLocalPlayer();
2335 std::string playername = player->getName();
2336 std::string oldpwd = translatePassword(playername, oldpassword);
2337 std::string newpwd = translatePassword(playername, newpassword);
2339 std::ostringstream os(std::ios_base::binary);
2340 u8 buf[2+PASSWORD_SIZE*2];
2342 [0] u16 TOSERVER_PASSWORD
2343 [2] u8[28] old password
2344 [30] u8[28] new password
2347 writeU16(buf, TOSERVER_PASSWORD);
2348 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2350 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2351 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2353 buf[2+PASSWORD_SIZE-1] = 0;
2354 buf[30+PASSWORD_SIZE-1] = 0;
2355 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2358 std::string s = os.str();
2359 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2361 Send(0, data, true);
2365 void Client::sendDamage(u8 damage)
2367 DSTACK(__FUNCTION_NAME);
2368 std::ostringstream os(std::ios_base::binary);
2370 writeU16(os, TOSERVER_DAMAGE);
2371 writeU8(os, damage);
2374 std::string s = os.str();
2375 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2377 Send(0, data, true);
2380 void Client::sendBreath(u16 breath)
2382 DSTACK(__FUNCTION_NAME);
2383 std::ostringstream os(std::ios_base::binary);
2385 writeU16(os, TOSERVER_BREATH);
2386 writeU16(os, breath);
2388 std::string s = os.str();
2389 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2391 Send(0, data, true);
2394 void Client::sendRespawn()
2396 DSTACK(__FUNCTION_NAME);
2397 std::ostringstream os(std::ios_base::binary);
2399 writeU16(os, TOSERVER_RESPAWN);
2402 std::string s = os.str();
2403 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2405 Send(0, data, true);
2408 void Client::sendPlayerPos()
2410 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2412 LocalPlayer *myplayer = m_env.getLocalPlayer();
2413 if(myplayer == NULL)
2416 // Save bandwidth by only updating position when something changed
2417 if(myplayer->last_position == myplayer->getPosition() &&
2418 myplayer->last_speed == myplayer->getSpeed() &&
2419 myplayer->last_pitch == myplayer->getPitch() &&
2420 myplayer->last_yaw == myplayer->getYaw() &&
2421 myplayer->last_keyPressed == myplayer->keyPressed)
2424 myplayer->last_position = myplayer->getPosition();
2425 myplayer->last_speed = myplayer->getSpeed();
2426 myplayer->last_pitch = myplayer->getPitch();
2427 myplayer->last_yaw = myplayer->getYaw();
2428 myplayer->last_keyPressed = myplayer->keyPressed;
2432 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2433 our_peer_id = m_con.GetPeerID();
2436 // Set peer id if not set already
2437 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2438 myplayer->peer_id = our_peer_id;
2439 // Check that an existing peer_id is the same as the connection's
2440 assert(myplayer->peer_id == our_peer_id);
2442 v3f pf = myplayer->getPosition();
2443 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2444 v3f sf = myplayer->getSpeed();
2445 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2446 s32 pitch = myplayer->getPitch() * 100;
2447 s32 yaw = myplayer->getYaw() * 100;
2448 u32 keyPressed=myplayer->keyPressed;
2452 [2] v3s32 position*100
2453 [2+12] v3s32 speed*100
2454 [2+12+12] s32 pitch*100
2455 [2+12+12+4] s32 yaw*100
2456 [2+12+12+4+4] u32 keyPressed
2458 SharedBuffer<u8> data(2+12+12+4+4+4);
2459 writeU16(&data[0], TOSERVER_PLAYERPOS);
2460 writeV3S32(&data[2], position);
2461 writeV3S32(&data[2+12], speed);
2462 writeS32(&data[2+12+12], pitch);
2463 writeS32(&data[2+12+12+4], yaw);
2464 writeU32(&data[2+12+12+4+4], keyPressed);
2465 // Send as unreliable
2466 Send(0, data, false);
2469 void Client::sendPlayerItem(u16 item)
2471 Player *myplayer = m_env.getLocalPlayer();
2472 if(myplayer == NULL)
2475 u16 our_peer_id = m_con.GetPeerID();
2477 // Set peer id if not set already
2478 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2479 myplayer->peer_id = our_peer_id;
2480 // Check that an existing peer_id is the same as the connection's
2481 assert(myplayer->peer_id == our_peer_id);
2483 SharedBuffer<u8> data(2+2);
2484 writeU16(&data[0], TOSERVER_PLAYERITEM);
2485 writeU16(&data[2], item);
2488 Send(0, data, true);
2491 void Client::removeNode(v3s16 p)
2493 std::map<v3s16, MapBlock*> modified_blocks;
2497 //TimeTaker t("removeNodeAndUpdate", m_device);
2498 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2500 catch(InvalidPositionException &e)
2504 // add urgent task to update the modified node
2505 addUpdateMeshTaskForNode(p, false, true);
2507 for(std::map<v3s16, MapBlock * >::iterator
2508 i = modified_blocks.begin();
2509 i != modified_blocks.end(); ++i)
2511 addUpdateMeshTaskWithEdge(i->first);
2515 void Client::addNode(v3s16 p, MapNode n)
2517 TimeTaker timer1("Client::addNode()");
2519 std::map<v3s16, MapBlock*> modified_blocks;
2523 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2524 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2526 catch(InvalidPositionException &e)
2529 for(std::map<v3s16, MapBlock * >::iterator
2530 i = modified_blocks.begin();
2531 i != modified_blocks.end(); ++i)
2533 addUpdateMeshTaskWithEdge(i->first);
2537 void Client::setPlayerControl(PlayerControl &control)
2539 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2540 LocalPlayer *player = m_env.getLocalPlayer();
2541 assert(player != NULL);
2542 player->control = control;
2545 void Client::selectPlayerItem(u16 item)
2547 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2548 m_playeritem = item;
2549 m_inventory_updated = true;
2550 sendPlayerItem(item);
2553 // Returns true if the inventory of the local player has been
2554 // updated from the server. If it is true, it is set to false.
2555 bool Client::getLocalInventoryUpdated()
2557 // m_inventory_updated is behind envlock
2558 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2559 bool updated = m_inventory_updated;
2560 m_inventory_updated = false;
2564 // Copies the inventory of the local player to parameter
2565 void Client::getLocalInventory(Inventory &dst)
2567 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2568 Player *player = m_env.getLocalPlayer();
2569 assert(player != NULL);
2570 dst = player->inventory;
2573 Inventory* Client::getInventory(const InventoryLocation &loc)
2576 case InventoryLocation::UNDEFINED:
2579 case InventoryLocation::CURRENT_PLAYER:
2581 Player *player = m_env.getLocalPlayer();
2582 assert(player != NULL);
2583 return &player->inventory;
2586 case InventoryLocation::PLAYER:
2588 Player *player = m_env.getPlayer(loc.name.c_str());
2591 return &player->inventory;
2594 case InventoryLocation::NODEMETA:
2596 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2599 return meta->getInventory();
2602 case InventoryLocation::DETACHED:
2604 if(m_detached_inventories.count(loc.name) == 0)
2606 return m_detached_inventories[loc.name];
2614 void Client::inventoryAction(InventoryAction *a)
2617 Send it to the server
2619 sendInventoryAction(a);
2622 Predict some local inventory changes
2624 a->clientApply(this, this);
2630 ClientActiveObject * Client::getSelectedActiveObject(
2632 v3f from_pos_f_on_map,
2633 core::line3d<f32> shootline_on_map
2636 std::vector<DistanceSortedActiveObject> objects;
2638 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2640 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2643 // After this, the closest object is the first in the array.
2644 std::sort(objects.begin(), objects.end());
2646 for(u32 i=0; i<objects.size(); i++)
2648 ClientActiveObject *obj = objects[i].obj;
2650 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2651 if(selection_box == NULL)
2654 v3f pos = obj->getPosition();
2656 core::aabbox3d<f32> offsetted_box(
2657 selection_box->MinEdge + pos,
2658 selection_box->MaxEdge + pos
2661 if(offsetted_box.intersectsWithLine(shootline_on_map))
2663 //infostream<<"Returning selected object"<<std::endl;
2668 //infostream<<"No object selected; returning NULL."<<std::endl;
2672 void Client::printDebugInfo(std::ostream &os)
2674 //JMutexAutoLock lock1(m_fetchblock_mutex);
2675 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2677 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2678 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2679 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2683 std::list<std::string> Client::getConnectedPlayerNames()
2685 return m_env.getPlayerNames();
2688 float Client::getAnimationTime()
2690 return m_animation_time;
2693 int Client::getCrackLevel()
2695 return m_crack_level;
2698 void Client::setCrack(int level, v3s16 pos)
2700 int old_crack_level = m_crack_level;
2701 v3s16 old_crack_pos = m_crack_pos;
2703 m_crack_level = level;
2706 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2709 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2711 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2714 addUpdateMeshTaskForNode(pos, false, true);
2720 Player *player = m_env.getLocalPlayer();
2721 assert(player != NULL);
2725 u16 Client::getBreath()
2727 Player *player = m_env.getLocalPlayer();
2728 assert(player != NULL);
2729 return player->getBreath();
2732 bool Client::getChatMessage(std::wstring &message)
2734 if(m_chat_queue.size() == 0)
2736 message = m_chat_queue.pop_front();
2740 void Client::typeChatMessage(const std::wstring &message)
2742 // Discard empty line
2747 sendChatMessage(message);
2750 if (message[0] == L'/')
2752 m_chat_queue.push_back(
2753 (std::wstring)L"issued command: "+message);
2757 LocalPlayer *player = m_env.getLocalPlayer();
2758 assert(player != NULL);
2759 std::wstring name = narrow_to_wide(player->getName());
2760 m_chat_queue.push_back(
2761 (std::wstring)L"<"+name+L"> "+message);
2765 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2767 /*infostream<<"Client::addUpdateMeshTask(): "
2768 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2769 <<" ack_to_server="<<ack_to_server
2770 <<" urgent="<<urgent
2773 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2778 Create a task to update the mesh of the block
2781 MeshMakeData *data = new MeshMakeData(this);
2784 //TimeTaker timer("data fill");
2786 // Debug: 1-6ms, avg=2ms
2788 data->setCrack(m_crack_level, m_crack_pos);
2789 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2793 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2795 // Add task to queue
2796 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2798 /*infostream<<"Mesh update input queue size is "
2799 <<m_mesh_update_thread.m_queue_in.size()
2803 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2807 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2808 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2813 v3s16 p = blockpos + v3s16(0,0,0);
2814 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2815 addUpdateMeshTask(p, ack_to_server, urgent);
2817 catch(InvalidPositionException &e){}
2819 for (int i=0;i<6;i++)
2822 v3s16 p = blockpos + g_6dirs[i];
2823 addUpdateMeshTask(p, false, urgent);
2825 catch(InvalidPositionException &e){}
2829 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2833 infostream<<"Client::addUpdateMeshTaskForNode(): "
2834 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2838 v3s16 blockpos = getNodeBlockPos(nodepos);
2839 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2842 v3s16 p = blockpos + v3s16(0,0,0);
2843 addUpdateMeshTask(p, ack_to_server, urgent);
2845 catch(InvalidPositionException &e){}
2847 if(nodepos.X == blockpos_relative.X){
2849 v3s16 p = blockpos + v3s16(-1,0,0);
2850 addUpdateMeshTask(p, false, urgent);
2852 catch(InvalidPositionException &e){}
2854 if(nodepos.Y == blockpos_relative.Y){
2856 v3s16 p = blockpos + v3s16(0,-1,0);
2857 addUpdateMeshTask(p, false, urgent);
2859 catch(InvalidPositionException &e){}
2861 if(nodepos.Z == blockpos_relative.Z){
2863 v3s16 p = blockpos + v3s16(0,0,-1);
2864 addUpdateMeshTask(p, false, urgent);
2866 catch(InvalidPositionException &e){}
2870 ClientEvent Client::getClientEvent()
2872 if(m_client_event_queue.size() == 0)
2875 event.type = CE_NONE;
2878 return m_client_event_queue.pop_front();
2881 void draw_load_screen(const std::wstring &text,
2882 IrrlichtDevice* device, gui::IGUIFont* font,
2883 float dtime=0 ,int percent=0, bool clouds=true);
2884 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2886 infostream<<"Client::afterContentReceived() started"<<std::endl;
2887 assert(m_itemdef_received);
2888 assert(m_nodedef_received);
2889 assert(texturesReceived());
2891 // remove the information about which checksum each texture
2893 m_media_name_sha1_map.clear();
2895 // Rebuild inherited images and recreate textures
2896 infostream<<"- Rebuilding images and textures"<<std::endl;
2897 m_tsrc->rebuildImagesAndTextures();
2900 infostream<<"- Rebuilding shaders"<<std::endl;
2901 m_shsrc->rebuildShaders();
2903 // Update node aliases
2904 infostream<<"- Updating node aliases"<<std::endl;
2905 m_nodedef->updateAliases(m_itemdef);
2907 // Update node textures
2908 infostream<<"- Updating node textures"<<std::endl;
2909 m_nodedef->updateTextures(m_tsrc);
2911 // Preload item textures and meshes if configured to
2912 if(g_settings->getBool("preload_item_visuals"))
2914 verbosestream<<"Updating item textures and meshes"<<std::endl;
2915 wchar_t* text = wgettext("Item textures...");
2916 draw_load_screen(text,device,font,0,0);
2917 std::set<std::string> names = m_itemdef->getAll();
2918 size_t size = names.size();
2921 for(std::set<std::string>::const_iterator
2922 i = names.begin(); i != names.end(); ++i){
2923 // Asking for these caches the result
2924 m_itemdef->getInventoryTexture(*i, this);
2925 m_itemdef->getWieldMesh(*i, this);
2927 percent = count*100/size;
2928 if (count%50 == 0) // only update every 50 item
2929 draw_load_screen(text,device,font,0,percent);
2934 // Start mesh update thread after setting up content definitions
2935 infostream<<"- Starting mesh update thread"<<std::endl;
2936 m_mesh_update_thread.Start();
2938 infostream<<"Client::afterContentReceived() done"<<std::endl;
2941 float Client::getRTT(void)
2944 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2945 } catch(con::PeerNotFoundException &e){
2950 // IGameDef interface
2952 IItemDefManager* Client::getItemDefManager()
2956 INodeDefManager* Client::getNodeDefManager()
2960 ICraftDefManager* Client::getCraftDefManager()
2963 //return m_craftdef;
2965 ITextureSource* Client::getTextureSource()
2969 IShaderSource* Client::getShaderSource()
2973 u16 Client::allocateUnknownNodeId(const std::string &name)
2975 errorstream<<"Client::allocateUnknownNodeId(): "
2976 <<"Client cannot allocate node IDs"<<std::endl;
2978 return CONTENT_IGNORE;
2980 ISoundManager* Client::getSoundManager()
2984 MtEventManager* Client::getEventManager()