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 "jthread/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"
54 #include <curl/curl.h>
57 static std::string getMediaCacheDir()
59 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
66 QueuedMeshUpdate::QueuedMeshUpdate():
69 ack_block_to_server(false)
73 QueuedMeshUpdate::~QueuedMeshUpdate()
83 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 curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
249 res = curl_easy_perform(curl);
250 if (res == CURLE_OK) {
251 std::string data = stream.str();
252 m_file_data.push_back(make_pair(i->name, data));
254 m_failed.push_back(*i);
255 infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
257 curl_easy_cleanup(curl);
261 END_DEBUG_EXCEPTION_HANDLER(errorstream)
267 IrrlichtDevice *device,
268 const char *playername,
269 std::string password,
270 MapDrawControl &control,
271 IWritableTextureSource *tsrc,
272 IWritableShaderSource *shsrc,
273 IWritableItemDefManager *itemdef,
274 IWritableNodeDefManager *nodedef,
275 ISoundManager *sound,
276 MtEventManager *event,
285 m_mesh_update_thread(this),
287 new ClientMap(this, this, control,
288 device->getSceneManager()->getRootSceneNode(),
289 device->getSceneManager(), 666),
290 device->getSceneManager(),
293 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
295 m_server_ser_ver(SER_FMT_VER_INVALID),
297 m_inventory_updated(false),
298 m_inventory_from_server(NULL),
299 m_inventory_from_server_age(0.0),
304 m_password(password),
305 m_access_denied(false),
306 m_media_cache(getMediaCacheDir()),
307 m_media_receive_started(false),
309 m_media_received_count(0),
310 m_itemdef_received(false),
311 m_nodedef_received(false),
312 m_time_of_day_set(false),
313 m_last_time_of_day_f(-1),
314 m_time_of_day_update_timer(0),
315 m_recommended_send_interval(0.1),
316 m_removed_sounds_check_timer(0)
318 m_packetcounter_timer = 0.0;
319 //m_delete_unused_sectors_timer = 0.0;
320 m_connection_reinit_timer = 0.0;
321 m_avg_rtt_timer = 0.0;
322 m_playerpos_send_timer = 0.0;
323 m_ignore_damage_timer = 0.0;
329 Player *player = new LocalPlayer(this);
331 player->updateName(playername);
333 m_env.addPlayer(player);
336 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
337 m_media_fetch_threads.push_back(new MediaFetchThread(this));
343 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
347 m_mesh_update_thread.setRun(false);
348 while(m_mesh_update_thread.IsRunning())
350 while(!m_mesh_update_thread.m_queue_out.empty()) {
351 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
356 delete m_inventory_from_server;
358 // Delete detached inventories
360 for(std::map<std::string, Inventory*>::iterator
361 i = m_detached_inventories.begin();
362 i != m_detached_inventories.end(); i++){
367 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
368 i != m_media_fetch_threads.end(); ++i)
371 // cleanup 3d model meshes on client shutdown
372 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
373 scene::IAnimatedMesh * mesh =
374 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
377 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
381 void Client::connect(Address address)
383 DSTACK(__FUNCTION_NAME);
384 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
385 m_con.SetTimeoutMs(0);
386 m_con.Connect(address);
389 bool Client::connectedAndInitialized()
391 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
393 if(m_con.Connected() == false)
396 if(m_server_ser_ver == SER_FMT_VER_INVALID)
402 void Client::step(float dtime)
404 DSTACK(__FUNCTION_NAME);
410 if(m_ignore_damage_timer > dtime)
411 m_ignore_damage_timer -= dtime;
413 m_ignore_damage_timer = 0.0;
415 m_animation_time += dtime;
416 if(m_animation_time > 60.0)
417 m_animation_time -= 60.0;
419 m_time_of_day_update_timer += dtime;
421 //infostream<<"Client steps "<<dtime<<std::endl;
424 //TimeTaker timer("ReceiveAll()", m_device);
430 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
432 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
433 m_con.RunTimeouts(dtime);
440 float &counter = m_packetcounter_timer;
446 infostream<<"Client packetcounter (20s):"<<std::endl;
447 m_packetcounter.print(infostream);
448 m_packetcounter.clear();
452 // Get connection status
453 bool connected = connectedAndInitialized();
458 Delete unused sectors
460 NOTE: This jams the game for a while because deleting sectors
464 float &counter = m_delete_unused_sectors_timer;
472 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
474 core::list<v3s16> deleted_blocks;
476 float delete_unused_sectors_timeout =
477 g_settings->getFloat("client_delete_unused_sectors_timeout");
479 // Delete sector blocks
480 /*u32 num = m_env.getMap().unloadUnusedData
481 (delete_unused_sectors_timeout,
482 true, &deleted_blocks);*/
484 // Delete whole sectors
485 m_env.getMap().unloadUnusedData
486 (delete_unused_sectors_timeout,
489 if(deleted_blocks.size() > 0)
491 /*infostream<<"Client: Deleted blocks of "<<num
492 <<" unused sectors"<<std::endl;*/
493 /*infostream<<"Client: Deleted "<<num
494 <<" unused sectors"<<std::endl;*/
500 // Env is locked so con can be locked.
501 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
503 core::list<v3s16>::Iterator i = deleted_blocks.begin();
504 core::list<v3s16> sendlist;
507 if(sendlist.size() == 255 || i == deleted_blocks.end())
509 if(sendlist.size() == 0)
518 u32 replysize = 2+1+6*sendlist.size();
519 SharedBuffer<u8> reply(replysize);
520 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
521 reply[2] = sendlist.size();
523 for(core::list<v3s16>::Iterator
524 j = sendlist.begin();
525 j != sendlist.end(); j++)
527 writeV3S16(&reply[2+1+6*k], *j);
530 m_con.Send(PEER_ID_SERVER, 1, reply, true);
532 if(i == deleted_blocks.end())
538 sendlist.push_back(*i);
546 if(connected == false)
548 float &counter = m_connection_reinit_timer;
554 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
556 Player *myplayer = m_env.getLocalPlayer();
557 assert(myplayer != NULL);
559 // Send TOSERVER_INIT
560 // [0] u16 TOSERVER_INIT
561 // [2] u8 SER_FMT_VER_HIGHEST_READ
562 // [3] u8[20] player_name
563 // [23] u8[28] password (new in some version)
564 // [51] u16 minimum supported network protocol version (added sometime)
565 // [53] u16 maximum supported network protocol version (added later than the previous one)
566 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
567 writeU16(&data[0], TOSERVER_INIT);
568 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
570 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
571 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
573 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
576 memset((char*)&data[23], 0, PASSWORD_SIZE);
577 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
579 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
580 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
582 // Send as unreliable
583 Send(0, data, false);
586 // Not connected, return
591 Do stuff if connected
595 Run Map's timers and unload unused data
597 const float map_timer_and_unload_dtime = 5.25;
598 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
600 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
601 std::list<v3s16> deleted_blocks;
602 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
603 g_settings->getFloat("client_unload_unused_data_timeout"),
606 /*if(deleted_blocks.size() > 0)
607 infostream<<"Client: Unloaded "<<deleted_blocks.size()
608 <<" unused blocks"<<std::endl;*/
612 NOTE: This loop is intentionally iterated the way it is.
615 std::list<v3s16>::iterator i = deleted_blocks.begin();
616 std::list<v3s16> sendlist;
619 if(sendlist.size() == 255 || i == deleted_blocks.end())
621 if(sendlist.size() == 0)
630 u32 replysize = 2+1+6*sendlist.size();
631 SharedBuffer<u8> reply(replysize);
632 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
633 reply[2] = sendlist.size();
635 for(std::list<v3s16>::iterator
636 j = sendlist.begin();
637 j != sendlist.end(); ++j)
639 writeV3S16(&reply[2+1+6*k], *j);
642 m_con.Send(PEER_ID_SERVER, 1, reply, true);
644 if(i == deleted_blocks.end())
650 sendlist.push_back(*i);
660 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
662 // Control local player (0ms)
663 LocalPlayer *player = m_env.getLocalPlayer();
664 assert(player != NULL);
665 player->applyControl(dtime);
667 //TimeTaker envtimer("env step", m_device);
676 ClientEnvEvent event = m_env.getClientEvent();
677 if(event.type == CEE_NONE)
681 else if(event.type == CEE_PLAYER_DAMAGE)
683 if(m_ignore_damage_timer <= 0)
685 u8 damage = event.player_damage.amount;
687 if(event.player_damage.send_to_server)
690 // Add to ClientEvent queue
692 event.type = CE_PLAYER_DAMAGE;
693 event.player_damage.amount = damage;
694 m_client_event_queue.push_back(event);
697 else if(event.type == CEE_PLAYER_BREATH)
699 u16 breath = event.player_breath.amount;
709 float &counter = m_avg_rtt_timer;
714 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
715 // connectedAndInitialized() is true, peer exists.
716 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
717 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
722 Send player position to server
725 float &counter = m_playerpos_send_timer;
727 if(counter >= m_recommended_send_interval)
735 Replace updated meshes
738 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
740 //TimeTaker timer("** Processing mesh update result queue");
743 /*infostream<<"Mesh update result queue size is "
744 <<m_mesh_update_thread.m_queue_out.size()
747 int num_processed_meshes = 0;
748 while(!m_mesh_update_thread.m_queue_out.empty())
750 num_processed_meshes++;
751 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
752 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
755 //JMutexAutoLock lock(block->mesh_mutex);
757 // Delete the old mesh
758 if(block->mesh != NULL)
760 // TODO: Remove hardware buffers of meshbuffers of block->mesh
765 // Replace with the new mesh
766 block->mesh = r.mesh;
770 if(r.ack_block_to_server)
772 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
773 <<","<<r.p.Z<<")"<<std::endl;*/
784 u32 replysize = 2+1+6;
785 SharedBuffer<u8> reply(replysize);
786 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
788 writeV3S16(&reply[3], r.p);
790 m_con.Send(PEER_ID_SERVER, 1, reply, true);
793 if(num_processed_meshes > 0)
794 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
800 if (m_media_receive_started) {
801 bool all_stopped = true;
802 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
803 thread != m_media_fetch_threads.end(); ++thread) {
804 all_stopped &= !(*thread)->IsRunning();
805 while (!(*thread)->m_file_data.empty()) {
806 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
807 if(m_media_received_count < m_media_count)
808 m_media_received_count++;
810 bool success = loadMedia(out.second, out.first);
812 verbosestream<<"Client: Loaded received media: "
813 <<"\""<<out.first<<"\". Caching."<<std::endl;
815 infostream<<"Client: Failed to load received media: "
816 <<"\""<<out.first<<"\". Not caching."<<std::endl;
820 bool did = fs::CreateAllDirs(getMediaCacheDir());
822 errorstream<<"Could not create media cache directory"
827 std::map<std::string, std::string>::iterator n;
828 n = m_media_name_sha1_map.find(out.first);
829 if(n == m_media_name_sha1_map.end())
830 errorstream<<"The server sent a file that has not "
831 <<"been announced."<<std::endl;
833 m_media_cache.update_sha1(out.second);
838 std::list<MediaRequest> fetch_failed;
839 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
840 thread != m_media_fetch_threads.end(); ++thread) {
841 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
842 request != (*thread)->m_failed.end(); ++request)
843 fetch_failed.push_back(*request);
844 (*thread)->m_failed.clear();
846 if (fetch_failed.size() > 0) {
847 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
848 << "Requesting them the usual way." << std::endl;
849 request_media(fetch_failed);
855 If the server didn't update the inventory in a while, revert
856 the local inventory (so the player notices the lag problem
857 and knows something is wrong).
859 if(m_inventory_from_server)
861 float interval = 10.0;
862 float count_before = floor(m_inventory_from_server_age / interval);
864 m_inventory_from_server_age += dtime;
866 float count_after = floor(m_inventory_from_server_age / interval);
868 if(count_after != count_before)
870 // Do this every <interval> seconds after TOCLIENT_INVENTORY
871 // Reset the locally changed inventory to the authoritative inventory
872 Player *player = m_env.getLocalPlayer();
873 player->inventory = *m_inventory_from_server;
874 m_inventory_updated = true;
879 Update positions of sounds attached to objects
882 for(std::map<int, u16>::iterator
883 i = m_sounds_to_objects.begin();
884 i != m_sounds_to_objects.end(); i++)
886 int client_id = i->first;
887 u16 object_id = i->second;
888 ClientActiveObject *cao = m_env.getActiveObject(object_id);
891 v3f pos = cao->getPosition();
892 m_sound->updateSoundPosition(client_id, pos);
897 Handle removed remotely initiated sounds
899 m_removed_sounds_check_timer += dtime;
900 if(m_removed_sounds_check_timer >= 2.32)
902 m_removed_sounds_check_timer = 0;
903 // Find removed sounds and clear references to them
904 std::set<s32> removed_server_ids;
905 for(std::map<s32, int>::iterator
906 i = m_sounds_server_to_client.begin();
907 i != m_sounds_server_to_client.end();)
909 s32 server_id = i->first;
910 int client_id = i->second;
912 if(!m_sound->soundExists(client_id)){
913 m_sounds_server_to_client.erase(server_id);
914 m_sounds_client_to_server.erase(client_id);
915 m_sounds_to_objects.erase(client_id);
916 removed_server_ids.insert(server_id);
920 if(removed_server_ids.size() != 0)
922 std::ostringstream os(std::ios_base::binary);
923 writeU16(os, TOSERVER_REMOVED_SOUNDS);
924 writeU16(os, removed_server_ids.size());
925 for(std::set<s32>::iterator i = removed_server_ids.begin();
926 i != removed_server_ids.end(); i++)
928 std::string s = os.str();
929 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
936 bool Client::loadMedia(const std::string &data, const std::string &filename)
938 // Silly irrlicht's const-incorrectness
939 Buffer<char> data_rw(data.c_str(), data.size());
943 const char *image_ext[] = {
944 ".png", ".jpg", ".bmp", ".tga",
945 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
948 name = removeStringEnd(filename, image_ext);
951 verbosestream<<"Client: Attempting to load image "
952 <<"file \""<<filename<<"\""<<std::endl;
954 io::IFileSystem *irrfs = m_device->getFileSystem();
955 video::IVideoDriver *vdrv = m_device->getVideoDriver();
957 // Create an irrlicht memory file
958 io::IReadFile *rfile = irrfs->createMemoryReadFile(
959 *data_rw, data_rw.getSize(), "_tempreadfile");
962 video::IImage *img = vdrv->createImageFromFile(rfile);
964 errorstream<<"Client: Cannot create image from data of "
965 <<"file \""<<filename<<"\""<<std::endl;
970 m_tsrc->insertSourceImage(filename, img);
977 const char *sound_ext[] = {
978 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
979 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
982 name = removeStringEnd(filename, sound_ext);
985 verbosestream<<"Client: Attempting to load sound "
986 <<"file \""<<filename<<"\""<<std::endl;
987 m_sound->loadSoundData(name, data);
991 const char *model_ext[] = {
992 ".x", ".b3d", ".md2", ".obj",
995 name = removeStringEnd(filename, model_ext);
998 verbosestream<<"Client: Storing model into Irrlicht: "
999 <<"\""<<filename<<"\""<<std::endl;
1000 scene::ISceneManager *smgr = m_device->getSceneManager();
1002 //check if mesh was already cached
1003 scene::IAnimatedMesh *mesh =
1004 smgr->getMeshCache()->getMeshByName(filename.c_str());
1007 errorstream << "Multiple models with name: " << filename.c_str() <<
1008 " found replacing previous model!" << std::endl;
1010 smgr->getMeshCache()->removeMesh(mesh);
1014 io::IFileSystem *irrfs = m_device->getFileSystem();
1015 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1016 *data_rw, data_rw.getSize(), filename.c_str());
1019 mesh = smgr->getMesh(rfile);
1020 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1025 errorstream<<"Client: Don't know how to load file \""
1026 <<filename<<"\""<<std::endl;
1030 // Virtual methods from con::PeerHandler
1031 void Client::peerAdded(con::Peer *peer)
1033 infostream<<"Client::peerAdded(): peer->id="
1034 <<peer->id<<std::endl;
1036 void Client::deletingPeer(con::Peer *peer, bool timeout)
1038 infostream<<"Client::deletingPeer(): "
1039 "Server Peer is getting deleted "
1040 <<"(timeout="<<timeout<<")"<<std::endl;
1045 u16 number of files requested
1051 void Client::request_media(const std::list<MediaRequest> &file_requests)
1053 std::ostringstream os(std::ios_base::binary);
1054 writeU16(os, TOSERVER_REQUEST_MEDIA);
1055 writeU16(os, file_requests.size());
1057 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1058 i != file_requests.end(); ++i) {
1059 os<<serializeString(i->name);
1063 std::string s = os.str();
1064 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1066 Send(0, data, true);
1067 infostream<<"Client: Sending media request list to server ("
1068 <<file_requests.size()<<" files)"<<std::endl;
1071 void Client::ReceiveAll()
1073 DSTACK(__FUNCTION_NAME);
1074 u32 start_ms = porting::getTimeMs();
1077 // Limit time even if there would be huge amounts of data to
1079 if(porting::getTimeMs() > start_ms + 100)
1084 g_profiler->graphAdd("client_received_packets", 1);
1086 catch(con::NoIncomingDataException &e)
1090 catch(con::InvalidIncomingDataException &e)
1092 infostream<<"Client::ReceiveAll(): "
1093 "InvalidIncomingDataException: what()="
1094 <<e.what()<<std::endl;
1099 void Client::Receive()
1101 DSTACK(__FUNCTION_NAME);
1102 SharedBuffer<u8> data;
1106 //TimeTaker t1("con mutex and receive", m_device);
1107 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1108 datasize = m_con.Receive(sender_peer_id, data);
1110 //TimeTaker t1("ProcessData", m_device);
1111 ProcessData(*data, datasize, sender_peer_id);
1115 sender_peer_id given to this shall be quaranteed to be a valid peer
1117 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1119 DSTACK(__FUNCTION_NAME);
1121 // Ignore packets that don't even fit a command
1124 m_packetcounter.add(60000);
1128 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1130 //infostream<<"Client: received command="<<command<<std::endl;
1131 m_packetcounter.add((u16)command);
1134 If this check is removed, be sure to change the queue
1135 system to know the ids
1137 if(sender_peer_id != PEER_ID_SERVER)
1139 infostream<<"Client::ProcessData(): Discarding data not "
1140 "coming from server: peer_id="<<sender_peer_id
1145 u8 ser_version = m_server_ser_ver;
1147 //infostream<<"Client received command="<<(int)command<<std::endl;
1149 if(command == TOCLIENT_INIT)
1154 u8 deployed = data[2];
1156 infostream<<"Client: TOCLIENT_INIT received with "
1157 "deployed="<<((int)deployed&0xff)<<std::endl;
1159 if(!ser_ver_supported(deployed))
1161 infostream<<"Client: TOCLIENT_INIT: Server sent "
1162 <<"unsupported ser_fmt_ver"<<std::endl;
1166 m_server_ser_ver = deployed;
1168 // Get player position
1169 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1170 if(datasize >= 2+1+6)
1171 playerpos_s16 = readV3S16(&data[2+1]);
1172 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1175 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1177 // Set player position
1178 Player *player = m_env.getLocalPlayer();
1179 assert(player != NULL);
1180 player->setPosition(playerpos_f);
1183 if(datasize >= 2+1+6+8)
1186 m_map_seed = readU64(&data[2+1+6]);
1187 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1190 if(datasize >= 2+1+6+8+4)
1193 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1194 infostream<<"Client: received recommended send interval "
1195 <<m_recommended_send_interval<<std::endl;
1200 SharedBuffer<u8> reply(replysize);
1201 writeU16(&reply[0], TOSERVER_INIT2);
1203 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1208 if(command == TOCLIENT_ACCESS_DENIED)
1210 // The server didn't like our password. Note, this needs
1211 // to be processed even if the serialisation format has
1212 // not been agreed yet, the same as TOCLIENT_INIT.
1213 m_access_denied = true;
1214 m_access_denied_reason = L"Unknown";
1217 std::string datastring((char*)&data[2], datasize-2);
1218 std::istringstream is(datastring, std::ios_base::binary);
1219 m_access_denied_reason = deSerializeWideString(is);
1224 if(ser_version == SER_FMT_VER_INVALID)
1226 infostream<<"Client: Server serialization"
1227 " format invalid or not initialized."
1228 " Skipping incoming command="<<command<<std::endl;
1232 // Just here to avoid putting the two if's together when
1233 // making some copypasta
1236 if(command == TOCLIENT_REMOVENODE)
1241 p.X = readS16(&data[2]);
1242 p.Y = readS16(&data[4]);
1243 p.Z = readS16(&data[6]);
1245 //TimeTaker t1("TOCLIENT_REMOVENODE");
1249 else if(command == TOCLIENT_ADDNODE)
1251 if(datasize < 8 + MapNode::serializedLength(ser_version))
1255 p.X = readS16(&data[2]);
1256 p.Y = readS16(&data[4]);
1257 p.Z = readS16(&data[6]);
1259 //TimeTaker t1("TOCLIENT_ADDNODE");
1262 n.deSerialize(&data[8], ser_version);
1264 bool remove_metadata = true;
1265 u32 index = 8 + MapNode::serializedLength(ser_version);
1266 if ((datasize >= index+1) && data[index]){
1267 remove_metadata = false;
1270 addNode(p, n, remove_metadata);
1272 else if(command == TOCLIENT_BLOCKDATA)
1274 // Ignore too small packet
1279 p.X = readS16(&data[2]);
1280 p.Y = readS16(&data[4]);
1281 p.Z = readS16(&data[6]);
1283 /*infostream<<"Client: Thread: BLOCKDATA for ("
1284 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1285 /*infostream<<"Client: Thread: BLOCKDATA for ("
1286 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1288 std::string datastring((char*)&data[8], datasize-8);
1289 std::istringstream istr(datastring, std::ios_base::binary);
1294 v2s16 p2d(p.X, p.Z);
1295 sector = m_env.getMap().emergeSector(p2d);
1297 assert(sector->getPos() == p2d);
1299 //TimeTaker timer("MapBlock deSerialize");
1302 block = sector->getBlockNoCreateNoEx(p.Y);
1306 Update an existing block
1308 //infostream<<"Updating"<<std::endl;
1309 block->deSerialize(istr, ser_version, false);
1310 block->deSerializeNetworkSpecific(istr);
1317 //infostream<<"Creating new"<<std::endl;
1318 block = new MapBlock(&m_env.getMap(), p, this);
1319 block->deSerialize(istr, ser_version, false);
1320 block->deSerializeNetworkSpecific(istr);
1321 sector->insertBlock(block);
1335 u32 replysize = 2+1+6;
1336 SharedBuffer<u8> reply(replysize);
1337 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1339 writeV3S16(&reply[3], p);
1341 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1345 Add it to mesh update queue and set it to be acknowledged after update.
1347 //infostream<<"Adding mesh update task for received block"<<std::endl;
1348 addUpdateMeshTaskWithEdge(p, true);
1350 else if(command == TOCLIENT_INVENTORY)
1355 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1358 //TimeTaker t2("mutex locking", m_device);
1359 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1362 //TimeTaker t3("istringstream init", m_device);
1363 std::string datastring((char*)&data[2], datasize-2);
1364 std::istringstream is(datastring, std::ios_base::binary);
1367 //TimeTaker t4("player get", m_device);
1368 Player *player = m_env.getLocalPlayer();
1369 assert(player != NULL);
1372 //TimeTaker t1("inventory.deSerialize()", m_device);
1373 player->inventory.deSerialize(is);
1376 m_inventory_updated = true;
1378 delete m_inventory_from_server;
1379 m_inventory_from_server = new Inventory(player->inventory);
1380 m_inventory_from_server_age = 0.0;
1382 //infostream<<"Client got player inventory:"<<std::endl;
1383 //player->inventory.print(infostream);
1386 else if(command == TOCLIENT_TIME_OF_DAY)
1391 u16 time_of_day = readU16(&data[2]);
1392 time_of_day = time_of_day % 24000;
1393 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1394 float time_speed = 0;
1395 if(datasize >= 2 + 2 + 4){
1396 time_speed = readF1000(&data[4]);
1398 // Old message; try to approximate speed of time by ourselves
1399 float time_of_day_f = (float)time_of_day / 24000.0;
1400 float tod_diff_f = 0;
1401 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1402 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1404 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1405 m_last_time_of_day_f = time_of_day_f;
1406 float time_diff = m_time_of_day_update_timer;
1407 m_time_of_day_update_timer = 0;
1408 if(m_time_of_day_set){
1409 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1410 infostream<<"Client: Measured time_of_day speed (old format): "
1411 <<time_speed<<" tod_diff_f="<<tod_diff_f
1412 <<" time_diff="<<time_diff<<std::endl;
1416 // Update environment
1417 m_env.setTimeOfDay(time_of_day);
1418 m_env.setTimeOfDaySpeed(time_speed);
1419 m_time_of_day_set = true;
1421 u32 dr = m_env.getDayNightRatio();
1422 verbosestream<<"Client: time_of_day="<<time_of_day
1423 <<" time_speed="<<time_speed
1424 <<" dr="<<dr<<std::endl;
1426 else if(command == TOCLIENT_CHAT_MESSAGE)
1434 std::string datastring((char*)&data[2], datasize-2);
1435 std::istringstream is(datastring, std::ios_base::binary);
1438 is.read((char*)buf, 2);
1439 u16 len = readU16(buf);
1441 std::wstring message;
1442 for(u16 i=0; i<len; i++)
1444 is.read((char*)buf, 2);
1445 message += (wchar_t)readU16(buf);
1448 /*infostream<<"Client received chat message: "
1449 <<wide_to_narrow(message)<<std::endl;*/
1451 m_chat_queue.push_back(message);
1453 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1455 //if(g_settings->getBool("enable_experimental"))
1459 u16 count of removed objects
1460 for all removed objects {
1463 u16 count of added objects
1464 for all added objects {
1467 u32 initialization data length
1468 string initialization data
1473 // Get all data except the command number
1474 std::string datastring((char*)&data[2], datasize-2);
1475 // Throw them in an istringstream
1476 std::istringstream is(datastring, std::ios_base::binary);
1480 // Read removed objects
1482 u16 removed_count = readU16((u8*)buf);
1483 for(u16 i=0; i<removed_count; i++)
1486 u16 id = readU16((u8*)buf);
1489 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1490 m_env.removeActiveObject(id);
1494 // Read added objects
1496 u16 added_count = readU16((u8*)buf);
1497 for(u16 i=0; i<added_count; i++)
1500 u16 id = readU16((u8*)buf);
1502 u8 type = readU8((u8*)buf);
1503 std::string data = deSerializeLongString(is);
1506 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1507 m_env.addActiveObject(id, type, data);
1512 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1514 //if(g_settings->getBool("enable_experimental"))
1526 // Get all data except the command number
1527 std::string datastring((char*)&data[2], datasize-2);
1528 // Throw them in an istringstream
1529 std::istringstream is(datastring, std::ios_base::binary);
1531 while(is.eof() == false)
1535 u16 id = readU16((u8*)buf);
1539 u16 message_size = readU16((u8*)buf);
1540 std::string message;
1541 message.reserve(message_size);
1542 for(u16 i=0; i<message_size; i++)
1545 message.append(buf, 1);
1547 // Pass on to the environment
1549 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1550 m_env.processActiveObjectMessage(id, message);
1555 else if(command == TOCLIENT_MOVEMENT)
1557 std::string datastring((char*)&data[2], datasize-2);
1558 std::istringstream is(datastring, std::ios_base::binary);
1559 Player *player = m_env.getLocalPlayer();
1560 assert(player != NULL);
1562 player->movement_acceleration_default = readF1000(is) * BS;
1563 player->movement_acceleration_air = readF1000(is) * BS;
1564 player->movement_acceleration_fast = readF1000(is) * BS;
1565 player->movement_speed_walk = readF1000(is) * BS;
1566 player->movement_speed_crouch = readF1000(is) * BS;
1567 player->movement_speed_fast = readF1000(is) * BS;
1568 player->movement_speed_climb = readF1000(is) * BS;
1569 player->movement_speed_jump = readF1000(is) * BS;
1570 player->movement_liquid_fluidity = readF1000(is) * BS;
1571 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1572 player->movement_liquid_sink = readF1000(is) * BS;
1573 player->movement_gravity = readF1000(is) * BS;
1575 else if(command == TOCLIENT_HP)
1577 std::string datastring((char*)&data[2], datasize-2);
1578 std::istringstream is(datastring, std::ios_base::binary);
1579 Player *player = m_env.getLocalPlayer();
1580 assert(player != NULL);
1581 u8 oldhp = player->hp;
1587 // Add to ClientEvent queue
1589 event.type = CE_PLAYER_DAMAGE;
1590 event.player_damage.amount = oldhp - hp;
1591 m_client_event_queue.push_back(event);
1594 else if(command == TOCLIENT_BREATH)
1596 std::string datastring((char*)&data[2], datasize-2);
1597 std::istringstream is(datastring, std::ios_base::binary);
1598 Player *player = m_env.getLocalPlayer();
1599 assert(player != NULL);
1600 u16 breath = readU16(is);
1601 player->setBreath(breath) ;
1603 else if(command == TOCLIENT_MOVE_PLAYER)
1605 std::string datastring((char*)&data[2], datasize-2);
1606 std::istringstream is(datastring, std::ios_base::binary);
1607 Player *player = m_env.getLocalPlayer();
1608 assert(player != NULL);
1609 v3f pos = readV3F1000(is);
1610 f32 pitch = readF1000(is);
1611 f32 yaw = readF1000(is);
1612 player->setPosition(pos);
1613 /*player->setPitch(pitch);
1614 player->setYaw(yaw);*/
1616 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1617 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1623 Add to ClientEvent queue.
1624 This has to be sent to the main program because otherwise
1625 it would just force the pitch and yaw values to whatever
1626 the camera points to.
1629 event.type = CE_PLAYER_FORCE_MOVE;
1630 event.player_force_move.pitch = pitch;
1631 event.player_force_move.yaw = yaw;
1632 m_client_event_queue.push_back(event);
1634 // Ignore damage for a few seconds, so that the player doesn't
1635 // get damage from falling on ground
1636 m_ignore_damage_timer = 3.0;
1638 else if(command == TOCLIENT_PLAYERITEM)
1640 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1642 else if(command == TOCLIENT_DEATHSCREEN)
1644 std::string datastring((char*)&data[2], datasize-2);
1645 std::istringstream is(datastring, std::ios_base::binary);
1647 bool set_camera_point_target = readU8(is);
1648 v3f camera_point_target = readV3F1000(is);
1651 event.type = CE_DEATHSCREEN;
1652 event.deathscreen.set_camera_point_target = set_camera_point_target;
1653 event.deathscreen.camera_point_target_x = camera_point_target.X;
1654 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1655 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1656 m_client_event_queue.push_back(event);
1658 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1660 std::string datastring((char*)&data[2], datasize-2);
1661 std::istringstream is(datastring, std::ios_base::binary);
1663 // Mesh update thread must be stopped while
1664 // updating content definitions
1665 assert(!m_mesh_update_thread.IsRunning());
1667 int num_files = readU16(is);
1669 infostream<<"Client: Received media announcement: packet size: "
1670 <<datasize<<std::endl;
1672 std::list<MediaRequest> file_requests;
1674 for(int i=0; i<num_files; i++)
1676 //read file from cache
1677 std::string name = deSerializeString(is);
1678 std::string sha1_base64 = deSerializeString(is);
1680 // if name contains illegal characters, ignore the file
1681 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1682 errorstream<<"Client: ignoring illegal file name "
1683 <<"sent by server: \""<<name<<"\""<<std::endl;
1687 std::string sha1_raw = base64_decode(sha1_base64);
1688 std::string sha1_hex = hex_encode(sha1_raw);
1689 std::ostringstream tmp_os(std::ios_base::binary);
1690 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1691 m_media_name_sha1_map[name] = sha1_raw;
1693 // If found in cache, try to load it from there
1696 bool success = loadMedia(tmp_os.str(), name);
1698 verbosestream<<"Client: Loaded cached media: "
1699 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1702 infostream<<"Client: Failed to load cached media: "
1703 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1706 // Didn't load from cache; queue it to be requested
1707 verbosestream<<"Client: Adding file to request list: \""
1708 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1709 file_requests.push_back(MediaRequest(name));
1712 std::string remote_media = "";
1714 remote_media = deSerializeString(is);
1716 catch(SerializationError) {
1717 // not supported by server or turned off
1720 m_media_count = file_requests.size();
1721 m_media_receive_started = true;
1723 if (remote_media == "" || !USE_CURL) {
1724 request_media(file_requests);
1727 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1728 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1729 i != file_requests.end(); ++i) {
1730 (*cur)->m_file_requests.push_back(*i);
1732 if (cur == m_media_fetch_threads.end())
1733 cur = m_media_fetch_threads.begin();
1735 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1736 i != m_media_fetch_threads.end(); ++i) {
1737 (*i)->m_remote_url = remote_media;
1742 // notify server we received everything
1743 std::ostringstream os(std::ios_base::binary);
1744 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1745 std::string s = os.str();
1746 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1748 Send(0, data, true);
1751 event.type = CE_TEXTURES_UPDATED;
1752 m_client_event_queue.push_back(event);
1754 else if(command == TOCLIENT_MEDIA)
1756 std::string datastring((char*)&data[2], datasize-2);
1757 std::istringstream is(datastring, std::ios_base::binary);
1761 u16 total number of file bunches
1762 u16 index of this bunch
1763 u32 number of files in this bunch
1771 int num_bunches = readU16(is);
1772 int bunch_i = readU16(is);
1773 u32 num_files = readU32(is);
1774 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1775 <<num_bunches<<" files="<<num_files
1776 <<" size="<<datasize<<std::endl;
1778 // Check total and received media count
1779 assert(m_media_received_count <= m_media_count);
1780 if (num_files > m_media_count - m_media_received_count) {
1781 errorstream<<"Client: Received more files than requested:"
1782 <<" total count="<<m_media_count
1783 <<" total received="<<m_media_received_count
1784 <<" bunch "<<bunch_i<<"/"<<num_bunches
1785 <<" files="<<num_files
1786 <<" size="<<datasize<<std::endl;
1787 num_files = m_media_count - m_media_received_count;
1792 // Mesh update thread must be stopped while
1793 // updating content definitions
1794 assert(!m_mesh_update_thread.IsRunning());
1796 for(u32 i=0; i<num_files; i++){
1797 assert(m_media_received_count < m_media_count);
1798 m_media_received_count++;
1799 std::string name = deSerializeString(is);
1800 std::string data = deSerializeLongString(is);
1802 // if name contains illegal characters, ignore the file
1803 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1804 errorstream<<"Client: ignoring illegal file name "
1805 <<"sent by server: \""<<name<<"\""<<std::endl;
1809 bool success = loadMedia(data, name);
1811 verbosestream<<"Client: Loaded received media: "
1812 <<"\""<<name<<"\". Caching."<<std::endl;
1814 infostream<<"Client: Failed to load received media: "
1815 <<"\""<<name<<"\". Not caching."<<std::endl;
1819 bool did = fs::CreateAllDirs(getMediaCacheDir());
1821 errorstream<<"Could not create media cache directory"
1826 std::map<std::string, std::string>::iterator n;
1827 n = m_media_name_sha1_map.find(name);
1828 if(n == m_media_name_sha1_map.end())
1829 errorstream<<"The server sent a file that has not "
1830 <<"been announced."<<std::endl;
1832 m_media_cache.update_sha1(data);
1837 event.type = CE_TEXTURES_UPDATED;
1838 m_client_event_queue.push_back(event);
1840 else if(command == TOCLIENT_TOOLDEF)
1842 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1844 else if(command == TOCLIENT_NODEDEF)
1846 infostream<<"Client: Received node definitions: packet size: "
1847 <<datasize<<std::endl;
1849 // Mesh update thread must be stopped while
1850 // updating content definitions
1851 assert(!m_mesh_update_thread.IsRunning());
1853 // Decompress node definitions
1854 std::string datastring((char*)&data[2], datasize-2);
1855 std::istringstream is(datastring, std::ios_base::binary);
1856 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1857 std::ostringstream tmp_os;
1858 decompressZlib(tmp_is, tmp_os);
1860 // Deserialize node definitions
1861 std::istringstream tmp_is2(tmp_os.str());
1862 m_nodedef->deSerialize(tmp_is2);
1863 m_nodedef_received = true;
1865 else if(command == TOCLIENT_CRAFTITEMDEF)
1867 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1869 else if(command == TOCLIENT_ITEMDEF)
1871 infostream<<"Client: Received item definitions: packet size: "
1872 <<datasize<<std::endl;
1874 // Mesh update thread must be stopped while
1875 // updating content definitions
1876 assert(!m_mesh_update_thread.IsRunning());
1878 // Decompress item definitions
1879 std::string datastring((char*)&data[2], datasize-2);
1880 std::istringstream is(datastring, std::ios_base::binary);
1881 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1882 std::ostringstream tmp_os;
1883 decompressZlib(tmp_is, tmp_os);
1885 // Deserialize node definitions
1886 std::istringstream tmp_is2(tmp_os.str());
1887 m_itemdef->deSerialize(tmp_is2);
1888 m_itemdef_received = true;
1890 else if(command == TOCLIENT_PLAY_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::string name = deSerializeString(is);
1897 float gain = readF1000(is);
1898 int type = readU8(is); // 0=local, 1=positional, 2=object
1899 v3f pos = readV3F1000(is);
1900 u16 object_id = readU16(is);
1901 bool loop = readU8(is);
1906 client_id = m_sound->playSound(name, loop, gain);
1908 case 1: // positional
1909 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1912 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1914 pos = cao->getPosition();
1915 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1916 // TODO: Set up sound to move with object
1921 if(client_id != -1){
1922 m_sounds_server_to_client[server_id] = client_id;
1923 m_sounds_client_to_server[client_id] = server_id;
1925 m_sounds_to_objects[client_id] = object_id;
1928 else if(command == TOCLIENT_STOP_SOUND)
1930 std::string datastring((char*)&data[2], datasize-2);
1931 std::istringstream is(datastring, std::ios_base::binary);
1933 s32 server_id = readS32(is);
1934 std::map<s32, int>::iterator i =
1935 m_sounds_server_to_client.find(server_id);
1936 if(i != m_sounds_server_to_client.end()){
1937 int client_id = i->second;
1938 m_sound->stopSound(client_id);
1941 else if(command == TOCLIENT_PRIVILEGES)
1943 std::string datastring((char*)&data[2], datasize-2);
1944 std::istringstream is(datastring, std::ios_base::binary);
1946 m_privileges.clear();
1947 infostream<<"Client: Privileges updated: ";
1948 u16 num_privileges = readU16(is);
1949 for(u16 i=0; i<num_privileges; i++){
1950 std::string priv = deSerializeString(is);
1951 m_privileges.insert(priv);
1952 infostream<<priv<<" ";
1954 infostream<<std::endl;
1956 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1958 std::string datastring((char*)&data[2], datasize-2);
1959 std::istringstream is(datastring, std::ios_base::binary);
1961 // Store formspec in LocalPlayer
1962 Player *player = m_env.getLocalPlayer();
1963 assert(player != NULL);
1964 player->inventory_formspec = deSerializeLongString(is);
1966 else if(command == TOCLIENT_DETACHED_INVENTORY)
1968 std::string datastring((char*)&data[2], datasize-2);
1969 std::istringstream is(datastring, std::ios_base::binary);
1971 std::string name = deSerializeString(is);
1973 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1975 Inventory *inv = NULL;
1976 if(m_detached_inventories.count(name) > 0)
1977 inv = m_detached_inventories[name];
1979 inv = new Inventory(m_itemdef);
1980 m_detached_inventories[name] = inv;
1982 inv->deSerialize(is);
1984 else if(command == TOCLIENT_SHOW_FORMSPEC)
1986 std::string datastring((char*)&data[2], datasize-2);
1987 std::istringstream is(datastring, std::ios_base::binary);
1989 std::string formspec = deSerializeLongString(is);
1990 std::string formname = deSerializeString(is);
1993 event.type = CE_SHOW_FORMSPEC;
1994 // pointer is required as event is a struct only!
1995 // adding a std:string to a struct isn't possible
1996 event.show_formspec.formspec = new std::string(formspec);
1997 event.show_formspec.formname = new std::string(formname);
1998 m_client_event_queue.push_back(event);
2000 else if(command == TOCLIENT_SPAWN_PARTICLE)
2002 std::string datastring((char*)&data[2], datasize-2);
2003 std::istringstream is(datastring, std::ios_base::binary);
2005 v3f pos = readV3F1000(is);
2006 v3f vel = readV3F1000(is);
2007 v3f acc = readV3F1000(is);
2008 float expirationtime = readF1000(is);
2009 float size = readF1000(is);
2010 bool collisiondetection = readU8(is);
2011 std::string texture = deSerializeLongString(is);
2014 event.type = CE_SPAWN_PARTICLE;
2015 event.spawn_particle.pos = new v3f (pos);
2016 event.spawn_particle.vel = new v3f (vel);
2017 event.spawn_particle.acc = new v3f (acc);
2019 event.spawn_particle.expirationtime = expirationtime;
2020 event.spawn_particle.size = size;
2021 event.spawn_particle.collisiondetection =
2023 event.spawn_particle.texture = new std::string(texture);
2025 m_client_event_queue.push_back(event);
2027 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2029 std::string datastring((char*)&data[2], datasize-2);
2030 std::istringstream is(datastring, std::ios_base::binary);
2032 u16 amount = readU16(is);
2033 float spawntime = readF1000(is);
2034 v3f minpos = readV3F1000(is);
2035 v3f maxpos = readV3F1000(is);
2036 v3f minvel = readV3F1000(is);
2037 v3f maxvel = readV3F1000(is);
2038 v3f minacc = readV3F1000(is);
2039 v3f maxacc = readV3F1000(is);
2040 float minexptime = readF1000(is);
2041 float maxexptime = readF1000(is);
2042 float minsize = readF1000(is);
2043 float maxsize = readF1000(is);
2044 bool collisiondetection = readU8(is);
2045 std::string texture = deSerializeLongString(is);
2046 u32 id = readU32(is);
2049 event.type = CE_ADD_PARTICLESPAWNER;
2050 event.add_particlespawner.amount = amount;
2051 event.add_particlespawner.spawntime = spawntime;
2053 event.add_particlespawner.minpos = new v3f (minpos);
2054 event.add_particlespawner.maxpos = new v3f (maxpos);
2055 event.add_particlespawner.minvel = new v3f (minvel);
2056 event.add_particlespawner.maxvel = new v3f (maxvel);
2057 event.add_particlespawner.minacc = new v3f (minacc);
2058 event.add_particlespawner.maxacc = new v3f (maxacc);
2060 event.add_particlespawner.minexptime = minexptime;
2061 event.add_particlespawner.maxexptime = maxexptime;
2062 event.add_particlespawner.minsize = minsize;
2063 event.add_particlespawner.maxsize = maxsize;
2064 event.add_particlespawner.collisiondetection = collisiondetection;
2065 event.add_particlespawner.texture = new std::string(texture);
2066 event.add_particlespawner.id = id;
2068 m_client_event_queue.push_back(event);
2070 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2072 std::string datastring((char*)&data[2], datasize-2);
2073 std::istringstream is(datastring, std::ios_base::binary);
2075 u32 id = readU16(is);
2078 event.type = CE_DELETE_PARTICLESPAWNER;
2079 event.delete_particlespawner.id = id;
2081 m_client_event_queue.push_back(event);
2083 else if(command == TOCLIENT_HUDADD)
2085 std::string datastring((char *)&data[2], datasize - 2);
2086 std::istringstream is(datastring, std::ios_base::binary);
2088 u32 id = readU32(is);
2089 u8 type = readU8(is);
2090 v2f pos = readV2F1000(is);
2091 std::string name = deSerializeString(is);
2092 v2f scale = readV2F1000(is);
2093 std::string text = deSerializeString(is);
2094 u32 number = readU32(is);
2095 u32 item = readU32(is);
2096 u32 dir = readU32(is);
2097 v2f align = readV2F1000(is);
2098 v2f offset = readV2F1000(is);
2101 event.type = CE_HUDADD;
2102 event.hudadd.id = id;
2103 event.hudadd.type = type;
2104 event.hudadd.pos = new v2f(pos);
2105 event.hudadd.name = new std::string(name);
2106 event.hudadd.scale = new v2f(scale);
2107 event.hudadd.text = new std::string(text);
2108 event.hudadd.number = number;
2109 event.hudadd.item = item;
2110 event.hudadd.dir = dir;
2111 event.hudadd.align = new v2f(align);
2112 event.hudadd.offset = new v2f(offset);
2113 m_client_event_queue.push_back(event);
2115 else if(command == TOCLIENT_HUDRM)
2117 std::string datastring((char *)&data[2], datasize - 2);
2118 std::istringstream is(datastring, std::ios_base::binary);
2120 u32 id = readU32(is);
2123 event.type = CE_HUDRM;
2124 event.hudrm.id = id;
2125 m_client_event_queue.push_back(event);
2127 else if(command == TOCLIENT_HUDCHANGE)
2133 std::string datastring((char *)&data[2], datasize - 2);
2134 std::istringstream is(datastring, std::ios_base::binary);
2136 u32 id = readU32(is);
2137 u8 stat = (HudElementStat)readU8(is);
2139 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2140 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2141 v2fdata = readV2F1000(is);
2142 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2143 sdata = deSerializeString(is);
2145 intdata = readU32(is);
2148 event.type = CE_HUDCHANGE;
2149 event.hudchange.id = id;
2150 event.hudchange.stat = (HudElementStat)stat;
2151 event.hudchange.v2fdata = new v2f(v2fdata);
2152 event.hudchange.sdata = new std::string(sdata);
2153 event.hudchange.data = intdata;
2154 m_client_event_queue.push_back(event);
2156 else if(command == TOCLIENT_HUD_SET_FLAGS)
2158 std::string datastring((char *)&data[2], datasize - 2);
2159 std::istringstream is(datastring, std::ios_base::binary);
2161 Player *player = m_env.getLocalPlayer();
2162 assert(player != NULL);
2164 u32 flags = readU32(is);
2165 u32 mask = readU32(is);
2167 player->hud_flags &= ~mask;
2168 player->hud_flags |= flags;
2170 else if(command == TOCLIENT_HUD_SET_PARAM)
2172 std::string datastring((char *)&data[2], datasize - 2);
2173 std::istringstream is(datastring, std::ios_base::binary);
2175 Player *player = m_env.getLocalPlayer();
2176 assert(player != NULL);
2178 u16 param = readU16(is);
2179 std::string value = deSerializeString(is);
2181 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2182 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2183 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2184 player->hud_hotbar_itemcount = hotbar_itemcount;
2185 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2186 ((LocalPlayer *) player)->hotbar_image = value;
2187 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2188 ((LocalPlayer *) player)->hotbar_selected_image = value;
2193 infostream<<"Client: Ignoring unknown command "
2194 <<command<<std::endl;
2198 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2200 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2201 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2204 void Client::interact(u8 action, const PointedThing& pointed)
2206 if(connectedAndInitialized() == false){
2207 infostream<<"Client::interact() "
2208 "cancelled (not connected)"
2213 std::ostringstream os(std::ios_base::binary);
2219 [5] u32 length of the next item
2220 [9] serialized PointedThing
2222 0: start digging (from undersurface) or use
2223 1: stop digging (all parameters ignored)
2224 2: digging completed
2225 3: place block or item (to abovesurface)
2228 writeU16(os, TOSERVER_INTERACT);
2229 writeU8(os, action);
2230 writeU16(os, getPlayerItem());
2231 std::ostringstream tmp_os(std::ios::binary);
2232 pointed.serialize(tmp_os);
2233 os<<serializeLongString(tmp_os.str());
2235 std::string s = os.str();
2236 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2239 Send(0, data, true);
2242 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2243 const std::map<std::string, std::string> &fields)
2245 std::ostringstream os(std::ios_base::binary);
2247 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2249 os<<serializeString(formname);
2250 writeU16(os, fields.size());
2251 for(std::map<std::string, std::string>::const_iterator
2252 i = fields.begin(); i != fields.end(); i++){
2253 const std::string &name = i->first;
2254 const std::string &value = i->second;
2255 os<<serializeString(name);
2256 os<<serializeLongString(value);
2260 std::string s = os.str();
2261 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2263 Send(0, data, true);
2266 void Client::sendInventoryFields(const std::string &formname,
2267 const std::map<std::string, std::string> &fields)
2269 std::ostringstream os(std::ios_base::binary);
2271 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2272 os<<serializeString(formname);
2273 writeU16(os, fields.size());
2274 for(std::map<std::string, std::string>::const_iterator
2275 i = fields.begin(); i != fields.end(); i++){
2276 const std::string &name = i->first;
2277 const std::string &value = i->second;
2278 os<<serializeString(name);
2279 os<<serializeLongString(value);
2283 std::string s = os.str();
2284 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2286 Send(0, data, true);
2289 void Client::sendInventoryAction(InventoryAction *a)
2291 std::ostringstream os(std::ios_base::binary);
2295 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2296 os.write((char*)buf, 2);
2301 std::string s = os.str();
2302 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2304 Send(0, data, true);
2307 void Client::sendChatMessage(const std::wstring &message)
2309 std::ostringstream os(std::ios_base::binary);
2313 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2314 os.write((char*)buf, 2);
2317 writeU16(buf, message.size());
2318 os.write((char*)buf, 2);
2321 for(u32 i=0; i<message.size(); i++)
2325 os.write((char*)buf, 2);
2329 std::string s = os.str();
2330 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2332 Send(0, data, true);
2335 void Client::sendChangePassword(const std::wstring oldpassword,
2336 const std::wstring newpassword)
2338 Player *player = m_env.getLocalPlayer();
2342 std::string playername = player->getName();
2343 std::string oldpwd = translatePassword(playername, oldpassword);
2344 std::string newpwd = translatePassword(playername, newpassword);
2346 std::ostringstream os(std::ios_base::binary);
2347 u8 buf[2+PASSWORD_SIZE*2];
2349 [0] u16 TOSERVER_PASSWORD
2350 [2] u8[28] old password
2351 [30] u8[28] new password
2354 writeU16(buf, TOSERVER_PASSWORD);
2355 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2357 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2358 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2360 buf[2+PASSWORD_SIZE-1] = 0;
2361 buf[30+PASSWORD_SIZE-1] = 0;
2362 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2365 std::string s = os.str();
2366 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2368 Send(0, data, true);
2372 void Client::sendDamage(u8 damage)
2374 DSTACK(__FUNCTION_NAME);
2375 std::ostringstream os(std::ios_base::binary);
2377 writeU16(os, TOSERVER_DAMAGE);
2378 writeU8(os, damage);
2381 std::string s = os.str();
2382 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2384 Send(0, data, true);
2387 void Client::sendBreath(u16 breath)
2389 DSTACK(__FUNCTION_NAME);
2390 std::ostringstream os(std::ios_base::binary);
2392 writeU16(os, TOSERVER_BREATH);
2393 writeU16(os, breath);
2395 std::string s = os.str();
2396 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2398 Send(0, data, true);
2401 void Client::sendRespawn()
2403 DSTACK(__FUNCTION_NAME);
2404 std::ostringstream os(std::ios_base::binary);
2406 writeU16(os, TOSERVER_RESPAWN);
2409 std::string s = os.str();
2410 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2412 Send(0, data, true);
2415 void Client::sendPlayerPos()
2417 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2419 LocalPlayer *myplayer = m_env.getLocalPlayer();
2420 if(myplayer == NULL)
2423 // Save bandwidth by only updating position when something changed
2424 if(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)
2431 myplayer->last_position = myplayer->getPosition();
2432 myplayer->last_speed = myplayer->getSpeed();
2433 myplayer->last_pitch = myplayer->getPitch();
2434 myplayer->last_yaw = myplayer->getYaw();
2435 myplayer->last_keyPressed = myplayer->keyPressed;
2439 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2440 our_peer_id = m_con.GetPeerID();
2443 // Set peer id if not set already
2444 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2445 myplayer->peer_id = our_peer_id;
2446 // Check that an existing peer_id is the same as the connection's
2447 assert(myplayer->peer_id == our_peer_id);
2449 v3f pf = myplayer->getPosition();
2450 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2451 v3f sf = myplayer->getSpeed();
2452 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2453 s32 pitch = myplayer->getPitch() * 100;
2454 s32 yaw = myplayer->getYaw() * 100;
2455 u32 keyPressed=myplayer->keyPressed;
2459 [2] v3s32 position*100
2460 [2+12] v3s32 speed*100
2461 [2+12+12] s32 pitch*100
2462 [2+12+12+4] s32 yaw*100
2463 [2+12+12+4+4] u32 keyPressed
2465 SharedBuffer<u8> data(2+12+12+4+4+4);
2466 writeU16(&data[0], TOSERVER_PLAYERPOS);
2467 writeV3S32(&data[2], position);
2468 writeV3S32(&data[2+12], speed);
2469 writeS32(&data[2+12+12], pitch);
2470 writeS32(&data[2+12+12+4], yaw);
2471 writeU32(&data[2+12+12+4+4], keyPressed);
2472 // Send as unreliable
2473 Send(0, data, false);
2476 void Client::sendPlayerItem(u16 item)
2478 Player *myplayer = m_env.getLocalPlayer();
2479 if(myplayer == NULL)
2482 u16 our_peer_id = m_con.GetPeerID();
2484 // Set peer id if not set already
2485 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2486 myplayer->peer_id = our_peer_id;
2487 // Check that an existing peer_id is the same as the connection's
2488 assert(myplayer->peer_id == our_peer_id);
2490 SharedBuffer<u8> data(2+2);
2491 writeU16(&data[0], TOSERVER_PLAYERITEM);
2492 writeU16(&data[2], item);
2495 Send(0, data, true);
2498 void Client::removeNode(v3s16 p)
2500 std::map<v3s16, MapBlock*> modified_blocks;
2504 //TimeTaker t("removeNodeAndUpdate", m_device);
2505 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2507 catch(InvalidPositionException &e)
2511 // add urgent task to update the modified node
2512 addUpdateMeshTaskForNode(p, false, true);
2514 for(std::map<v3s16, MapBlock * >::iterator
2515 i = modified_blocks.begin();
2516 i != modified_blocks.end(); ++i)
2518 addUpdateMeshTaskWithEdge(i->first);
2522 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2524 TimeTaker timer1("Client::addNode()");
2526 std::map<v3s16, MapBlock*> modified_blocks;
2530 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2531 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2533 catch(InvalidPositionException &e)
2536 for(std::map<v3s16, MapBlock * >::iterator
2537 i = modified_blocks.begin();
2538 i != modified_blocks.end(); ++i)
2540 addUpdateMeshTaskWithEdge(i->first);
2544 void Client::setPlayerControl(PlayerControl &control)
2546 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2547 LocalPlayer *player = m_env.getLocalPlayer();
2548 assert(player != NULL);
2549 player->control = control;
2552 void Client::selectPlayerItem(u16 item)
2554 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2555 m_playeritem = item;
2556 m_inventory_updated = true;
2557 sendPlayerItem(item);
2560 // Returns true if the inventory of the local player has been
2561 // updated from the server. If it is true, it is set to false.
2562 bool Client::getLocalInventoryUpdated()
2564 // m_inventory_updated is behind envlock
2565 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2566 bool updated = m_inventory_updated;
2567 m_inventory_updated = false;
2571 // Copies the inventory of the local player to parameter
2572 void Client::getLocalInventory(Inventory &dst)
2574 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2575 Player *player = m_env.getLocalPlayer();
2576 assert(player != NULL);
2577 dst = player->inventory;
2580 Inventory* Client::getInventory(const InventoryLocation &loc)
2583 case InventoryLocation::UNDEFINED:
2586 case InventoryLocation::CURRENT_PLAYER:
2588 Player *player = m_env.getLocalPlayer();
2589 assert(player != NULL);
2590 return &player->inventory;
2593 case InventoryLocation::PLAYER:
2595 Player *player = m_env.getPlayer(loc.name.c_str());
2598 return &player->inventory;
2601 case InventoryLocation::NODEMETA:
2603 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2606 return meta->getInventory();
2609 case InventoryLocation::DETACHED:
2611 if(m_detached_inventories.count(loc.name) == 0)
2613 return m_detached_inventories[loc.name];
2621 void Client::inventoryAction(InventoryAction *a)
2624 Send it to the server
2626 sendInventoryAction(a);
2629 Predict some local inventory changes
2631 a->clientApply(this, this);
2637 ClientActiveObject * Client::getSelectedActiveObject(
2639 v3f from_pos_f_on_map,
2640 core::line3d<f32> shootline_on_map
2643 std::vector<DistanceSortedActiveObject> objects;
2645 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2647 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2650 // After this, the closest object is the first in the array.
2651 std::sort(objects.begin(), objects.end());
2653 for(u32 i=0; i<objects.size(); i++)
2655 ClientActiveObject *obj = objects[i].obj;
2657 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2658 if(selection_box == NULL)
2661 v3f pos = obj->getPosition();
2663 core::aabbox3d<f32> offsetted_box(
2664 selection_box->MinEdge + pos,
2665 selection_box->MaxEdge + pos
2668 if(offsetted_box.intersectsWithLine(shootline_on_map))
2670 //infostream<<"Returning selected object"<<std::endl;
2675 //infostream<<"No object selected; returning NULL."<<std::endl;
2679 void Client::printDebugInfo(std::ostream &os)
2681 //JMutexAutoLock lock1(m_fetchblock_mutex);
2682 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2684 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2685 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2686 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2690 std::list<std::string> Client::getConnectedPlayerNames()
2692 return m_env.getPlayerNames();
2695 float Client::getAnimationTime()
2697 return m_animation_time;
2700 int Client::getCrackLevel()
2702 return m_crack_level;
2705 void Client::setCrack(int level, v3s16 pos)
2707 int old_crack_level = m_crack_level;
2708 v3s16 old_crack_pos = m_crack_pos;
2710 m_crack_level = level;
2713 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2716 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2718 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2721 addUpdateMeshTaskForNode(pos, false, true);
2727 Player *player = m_env.getLocalPlayer();
2728 assert(player != NULL);
2732 u16 Client::getBreath()
2734 Player *player = m_env.getLocalPlayer();
2735 assert(player != NULL);
2736 return player->getBreath();
2739 bool Client::getChatMessage(std::wstring &message)
2741 if(m_chat_queue.size() == 0)
2743 message = m_chat_queue.pop_front();
2747 void Client::typeChatMessage(const std::wstring &message)
2749 // Discard empty line
2754 sendChatMessage(message);
2757 if (message[0] == L'/')
2759 m_chat_queue.push_back(
2760 (std::wstring)L"issued command: "+message);
2764 LocalPlayer *player = m_env.getLocalPlayer();
2765 assert(player != NULL);
2766 std::wstring name = narrow_to_wide(player->getName());
2767 m_chat_queue.push_back(
2768 (std::wstring)L"<"+name+L"> "+message);
2772 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2774 /*infostream<<"Client::addUpdateMeshTask(): "
2775 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2776 <<" ack_to_server="<<ack_to_server
2777 <<" urgent="<<urgent
2780 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2785 Create a task to update the mesh of the block
2788 MeshMakeData *data = new MeshMakeData(this);
2791 //TimeTaker timer("data fill");
2793 // Debug: 1-6ms, avg=2ms
2795 data->setCrack(m_crack_level, m_crack_pos);
2796 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2800 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2802 // Add task to queue
2803 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2805 /*infostream<<"Mesh update input queue size is "
2806 <<m_mesh_update_thread.m_queue_in.size()
2810 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2814 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2815 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2820 v3s16 p = blockpos + v3s16(0,0,0);
2821 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2822 addUpdateMeshTask(p, ack_to_server, urgent);
2824 catch(InvalidPositionException &e){}
2826 for (int i=0;i<6;i++)
2829 v3s16 p = blockpos + g_6dirs[i];
2830 addUpdateMeshTask(p, false, urgent);
2832 catch(InvalidPositionException &e){}
2836 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2840 infostream<<"Client::addUpdateMeshTaskForNode(): "
2841 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2845 v3s16 blockpos = getNodeBlockPos(nodepos);
2846 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2849 v3s16 p = blockpos + v3s16(0,0,0);
2850 addUpdateMeshTask(p, ack_to_server, urgent);
2852 catch(InvalidPositionException &e){}
2854 if(nodepos.X == blockpos_relative.X){
2856 v3s16 p = blockpos + v3s16(-1,0,0);
2857 addUpdateMeshTask(p, false, urgent);
2859 catch(InvalidPositionException &e){}
2861 if(nodepos.Y == blockpos_relative.Y){
2863 v3s16 p = blockpos + v3s16(0,-1,0);
2864 addUpdateMeshTask(p, false, urgent);
2866 catch(InvalidPositionException &e){}
2868 if(nodepos.Z == blockpos_relative.Z){
2870 v3s16 p = blockpos + v3s16(0,0,-1);
2871 addUpdateMeshTask(p, false, urgent);
2873 catch(InvalidPositionException &e){}
2877 ClientEvent Client::getClientEvent()
2879 if(m_client_event_queue.size() == 0)
2882 event.type = CE_NONE;
2885 return m_client_event_queue.pop_front();
2888 void draw_load_screen(const std::wstring &text,
2889 IrrlichtDevice* device, gui::IGUIFont* font,
2890 float dtime=0 ,int percent=0, bool clouds=true);
2891 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2893 infostream<<"Client::afterContentReceived() started"<<std::endl;
2894 assert(m_itemdef_received);
2895 assert(m_nodedef_received);
2896 assert(texturesReceived());
2898 // remove the information about which checksum each texture
2900 m_media_name_sha1_map.clear();
2902 // Rebuild inherited images and recreate textures
2903 infostream<<"- Rebuilding images and textures"<<std::endl;
2904 m_tsrc->rebuildImagesAndTextures();
2907 infostream<<"- Rebuilding shaders"<<std::endl;
2908 m_shsrc->rebuildShaders();
2910 // Update node aliases
2911 infostream<<"- Updating node aliases"<<std::endl;
2912 m_nodedef->updateAliases(m_itemdef);
2914 // Update node textures
2915 infostream<<"- Updating node textures"<<std::endl;
2916 m_nodedef->updateTextures(m_tsrc);
2918 // Preload item textures and meshes if configured to
2919 if(g_settings->getBool("preload_item_visuals"))
2921 verbosestream<<"Updating item textures and meshes"<<std::endl;
2922 wchar_t* text = wgettext("Item textures...");
2923 draw_load_screen(text,device,font,0,0);
2924 std::set<std::string> names = m_itemdef->getAll();
2925 size_t size = names.size();
2928 for(std::set<std::string>::const_iterator
2929 i = names.begin(); i != names.end(); ++i){
2930 // Asking for these caches the result
2931 m_itemdef->getInventoryTexture(*i, this);
2932 m_itemdef->getWieldMesh(*i, this);
2934 percent = count*100/size;
2935 if (count%50 == 0) // only update every 50 item
2936 draw_load_screen(text,device,font,0,percent);
2941 // Start mesh update thread after setting up content definitions
2942 infostream<<"- Starting mesh update thread"<<std::endl;
2943 m_mesh_update_thread.Start();
2945 infostream<<"Client::afterContentReceived() done"<<std::endl;
2948 float Client::getRTT(void)
2951 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2952 } catch(con::PeerNotFoundException &e){
2957 // IGameDef interface
2959 IItemDefManager* Client::getItemDefManager()
2963 INodeDefManager* Client::getNodeDefManager()
2967 ICraftDefManager* Client::getCraftDefManager()
2970 //return m_craftdef;
2972 ITextureSource* Client::getTextureSource()
2976 IShaderSource* Client::getShaderSource()
2980 u16 Client::allocateUnknownNodeId(const std::string &name)
2982 errorstream<<"Client::allocateUnknownNodeId(): "
2983 <<"Client cannot allocate node IDs"<<std::endl;
2985 return CONTENT_IGNORE;
2987 ISoundManager* Client::getSoundManager()
2991 MtEventManager* Client::getEventManager()