3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "clientserver.h"
23 #include "jmutexautolock.h"
27 #include "mapsector.h"
28 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
38 #include <IFileSystem.h>
41 #include "clientmap.h"
42 #include "filecache.h"
44 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "util/serialize.h"
49 #include "util/directiontables.h"
52 #include <curl/curl.h>
55 static std::string getMediaCacheDir()
57 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 void * MediaFetchThread::Thread()
228 log_register_thread("MediaFetchThread");
230 DSTACK(__FUNCTION_NAME);
232 BEGIN_DEBUG_EXCEPTION_HANDLER
237 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
238 i != m_file_requests.end(); ++i) {
239 curl = curl_easy_init();
241 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
242 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
243 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
244 std::ostringstream stream;
245 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
246 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
247 res = curl_easy_perform(curl);
248 if (res == CURLE_OK) {
249 std::string data = stream.str();
250 m_file_data.push_back(make_pair(i->name, data));
252 m_failed.push_back(*i);
253 infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
255 curl_easy_cleanup(curl);
259 END_DEBUG_EXCEPTION_HANDLER(errorstream)
265 IrrlichtDevice *device,
266 const char *playername,
267 std::string password,
268 MapDrawControl &control,
269 IWritableTextureSource *tsrc,
270 IWritableShaderSource *shsrc,
271 IWritableItemDefManager *itemdef,
272 IWritableNodeDefManager *nodedef,
273 ISoundManager *sound,
274 MtEventManager *event,
283 m_mesh_update_thread(this),
285 new ClientMap(this, this, control,
286 device->getSceneManager()->getRootSceneNode(),
287 device->getSceneManager(), 666),
288 device->getSceneManager(),
291 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
293 m_server_ser_ver(SER_FMT_VER_INVALID),
295 m_inventory_updated(false),
296 m_inventory_from_server(NULL),
297 m_inventory_from_server_age(0.0),
302 m_password(password),
303 m_access_denied(false),
304 m_media_cache(getMediaCacheDir()),
305 m_media_receive_started(false),
307 m_media_received_count(0),
308 m_itemdef_received(false),
309 m_nodedef_received(false),
310 m_time_of_day_set(false),
311 m_last_time_of_day_f(-1),
312 m_time_of_day_update_timer(0),
313 m_recommended_send_interval(0.1),
314 m_removed_sounds_check_timer(0)
316 m_packetcounter_timer = 0.0;
317 //m_delete_unused_sectors_timer = 0.0;
318 m_connection_reinit_timer = 0.0;
319 m_avg_rtt_timer = 0.0;
320 m_playerpos_send_timer = 0.0;
321 m_ignore_damage_timer = 0.0;
327 Player *player = new LocalPlayer(this);
329 player->updateName(playername);
331 m_env.addPlayer(player);
334 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
335 m_media_fetch_threads.push_back(new MediaFetchThread(this));
341 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
345 m_mesh_update_thread.setRun(false);
346 while(m_mesh_update_thread.IsRunning())
348 while(!m_mesh_update_thread.m_queue_out.empty()) {
349 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
354 delete m_inventory_from_server;
356 // Delete detached inventories
358 for(std::map<std::string, Inventory*>::iterator
359 i = m_detached_inventories.begin();
360 i != m_detached_inventories.end(); i++){
365 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
366 i != m_media_fetch_threads.end(); ++i)
369 // cleanup 3d model meshes on client shutdown
370 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
371 scene::IAnimatedMesh * mesh =
372 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
375 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
379 void Client::connect(Address address)
381 DSTACK(__FUNCTION_NAME);
382 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
383 m_con.SetTimeoutMs(0);
384 m_con.Connect(address);
387 bool Client::connectedAndInitialized()
389 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
391 if(m_con.Connected() == false)
394 if(m_server_ser_ver == SER_FMT_VER_INVALID)
400 void Client::step(float dtime)
402 DSTACK(__FUNCTION_NAME);
408 if(m_ignore_damage_timer > dtime)
409 m_ignore_damage_timer -= dtime;
411 m_ignore_damage_timer = 0.0;
413 m_animation_time += dtime;
414 if(m_animation_time > 60.0)
415 m_animation_time -= 60.0;
417 m_time_of_day_update_timer += dtime;
419 //infostream<<"Client steps "<<dtime<<std::endl;
422 //TimeTaker timer("ReceiveAll()", m_device);
428 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
430 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
431 m_con.RunTimeouts(dtime);
438 float &counter = m_packetcounter_timer;
444 infostream<<"Client packetcounter (20s):"<<std::endl;
445 m_packetcounter.print(infostream);
446 m_packetcounter.clear();
450 // Get connection status
451 bool connected = connectedAndInitialized();
456 Delete unused sectors
458 NOTE: This jams the game for a while because deleting sectors
462 float &counter = m_delete_unused_sectors_timer;
470 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
472 core::list<v3s16> deleted_blocks;
474 float delete_unused_sectors_timeout =
475 g_settings->getFloat("client_delete_unused_sectors_timeout");
477 // Delete sector blocks
478 /*u32 num = m_env.getMap().unloadUnusedData
479 (delete_unused_sectors_timeout,
480 true, &deleted_blocks);*/
482 // Delete whole sectors
483 m_env.getMap().unloadUnusedData
484 (delete_unused_sectors_timeout,
487 if(deleted_blocks.size() > 0)
489 /*infostream<<"Client: Deleted blocks of "<<num
490 <<" unused sectors"<<std::endl;*/
491 /*infostream<<"Client: Deleted "<<num
492 <<" unused sectors"<<std::endl;*/
498 // Env is locked so con can be locked.
499 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
501 core::list<v3s16>::Iterator i = deleted_blocks.begin();
502 core::list<v3s16> sendlist;
505 if(sendlist.size() == 255 || i == deleted_blocks.end())
507 if(sendlist.size() == 0)
516 u32 replysize = 2+1+6*sendlist.size();
517 SharedBuffer<u8> reply(replysize);
518 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
519 reply[2] = sendlist.size();
521 for(core::list<v3s16>::Iterator
522 j = sendlist.begin();
523 j != sendlist.end(); j++)
525 writeV3S16(&reply[2+1+6*k], *j);
528 m_con.Send(PEER_ID_SERVER, 1, reply, true);
530 if(i == deleted_blocks.end())
536 sendlist.push_back(*i);
544 if(connected == false)
546 float &counter = m_connection_reinit_timer;
552 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
554 Player *myplayer = m_env.getLocalPlayer();
555 assert(myplayer != NULL);
557 // Send TOSERVER_INIT
558 // [0] u16 TOSERVER_INIT
559 // [2] u8 SER_FMT_VER_HIGHEST_READ
560 // [3] u8[20] player_name
561 // [23] u8[28] password (new in some version)
562 // [51] u16 minimum supported network protocol version (added sometime)
563 // [53] u16 maximum supported network protocol version (added later than the previous one)
564 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
565 writeU16(&data[0], TOSERVER_INIT);
566 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
568 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
569 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
571 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
574 memset((char*)&data[23], 0, PASSWORD_SIZE);
575 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
577 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
578 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
580 // Send as unreliable
581 Send(0, data, false);
584 // Not connected, return
589 Do stuff if connected
593 Run Map's timers and unload unused data
595 const float map_timer_and_unload_dtime = 5.25;
596 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
598 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
599 std::list<v3s16> deleted_blocks;
600 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
601 g_settings->getFloat("client_unload_unused_data_timeout"),
604 /*if(deleted_blocks.size() > 0)
605 infostream<<"Client: Unloaded "<<deleted_blocks.size()
606 <<" unused blocks"<<std::endl;*/
610 NOTE: This loop is intentionally iterated the way it is.
613 std::list<v3s16>::iterator i = deleted_blocks.begin();
614 std::list<v3s16> sendlist;
617 if(sendlist.size() == 255 || i == deleted_blocks.end())
619 if(sendlist.size() == 0)
628 u32 replysize = 2+1+6*sendlist.size();
629 SharedBuffer<u8> reply(replysize);
630 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
631 reply[2] = sendlist.size();
633 for(std::list<v3s16>::iterator
634 j = sendlist.begin();
635 j != sendlist.end(); ++j)
637 writeV3S16(&reply[2+1+6*k], *j);
640 m_con.Send(PEER_ID_SERVER, 1, reply, true);
642 if(i == deleted_blocks.end())
648 sendlist.push_back(*i);
658 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
660 // Control local player (0ms)
661 LocalPlayer *player = m_env.getLocalPlayer();
662 assert(player != NULL);
663 player->applyControl(dtime);
665 //TimeTaker envtimer("env step", m_device);
674 ClientEnvEvent event = m_env.getClientEvent();
675 if(event.type == CEE_NONE)
679 else if(event.type == CEE_PLAYER_DAMAGE)
681 if(m_ignore_damage_timer <= 0)
683 u8 damage = event.player_damage.amount;
685 if(event.player_damage.send_to_server)
688 // Add to ClientEvent queue
690 event.type = CE_PLAYER_DAMAGE;
691 event.player_damage.amount = damage;
692 m_client_event_queue.push_back(event);
695 else if(event.type == CEE_PLAYER_BREATH)
697 u16 breath = event.player_breath.amount;
707 float &counter = m_avg_rtt_timer;
712 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
713 // connectedAndInitialized() is true, peer exists.
714 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
715 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
720 Send player position to server
723 float &counter = m_playerpos_send_timer;
725 if(counter >= m_recommended_send_interval)
733 Replace updated meshes
736 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
738 //TimeTaker timer("** Processing mesh update result queue");
741 /*infostream<<"Mesh update result queue size is "
742 <<m_mesh_update_thread.m_queue_out.size()
745 int num_processed_meshes = 0;
746 while(!m_mesh_update_thread.m_queue_out.empty())
748 num_processed_meshes++;
749 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
750 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
753 //JMutexAutoLock lock(block->mesh_mutex);
755 // Delete the old mesh
756 if(block->mesh != NULL)
758 // TODO: Remove hardware buffers of meshbuffers of block->mesh
763 // Replace with the new mesh
764 block->mesh = r.mesh;
768 if(r.ack_block_to_server)
770 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
771 <<","<<r.p.Z<<")"<<std::endl;*/
782 u32 replysize = 2+1+6;
783 SharedBuffer<u8> reply(replysize);
784 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
786 writeV3S16(&reply[3], r.p);
788 m_con.Send(PEER_ID_SERVER, 1, reply, true);
791 if(num_processed_meshes > 0)
792 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
798 if (m_media_receive_started) {
799 bool all_stopped = true;
800 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
801 thread != m_media_fetch_threads.end(); ++thread) {
802 all_stopped &= !(*thread)->IsRunning();
803 while (!(*thread)->m_file_data.empty()) {
804 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
805 if(m_media_received_count < m_media_count)
806 m_media_received_count++;
808 bool success = loadMedia(out.second, out.first);
810 verbosestream<<"Client: Loaded received media: "
811 <<"\""<<out.first<<"\". Caching."<<std::endl;
813 infostream<<"Client: Failed to load received media: "
814 <<"\""<<out.first<<"\". Not caching."<<std::endl;
818 bool did = fs::CreateAllDirs(getMediaCacheDir());
820 errorstream<<"Could not create media cache directory"
825 std::map<std::string, std::string>::iterator n;
826 n = m_media_name_sha1_map.find(out.first);
827 if(n == m_media_name_sha1_map.end())
828 errorstream<<"The server sent a file that has not "
829 <<"been announced."<<std::endl;
831 m_media_cache.update_sha1(out.second);
836 std::list<MediaRequest> fetch_failed;
837 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
838 thread != m_media_fetch_threads.end(); ++thread) {
839 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
840 request != (*thread)->m_failed.end(); ++request)
841 fetch_failed.push_back(*request);
842 (*thread)->m_failed.clear();
844 if (fetch_failed.size() > 0) {
845 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
846 << "Requesting them the usual way." << std::endl;
847 request_media(fetch_failed);
853 If the server didn't update the inventory in a while, revert
854 the local inventory (so the player notices the lag problem
855 and knows something is wrong).
857 if(m_inventory_from_server)
859 float interval = 10.0;
860 float count_before = floor(m_inventory_from_server_age / interval);
862 m_inventory_from_server_age += dtime;
864 float count_after = floor(m_inventory_from_server_age / interval);
866 if(count_after != count_before)
868 // Do this every <interval> seconds after TOCLIENT_INVENTORY
869 // Reset the locally changed inventory to the authoritative inventory
870 Player *player = m_env.getLocalPlayer();
871 player->inventory = *m_inventory_from_server;
872 m_inventory_updated = true;
877 Update positions of sounds attached to objects
880 for(std::map<int, u16>::iterator
881 i = m_sounds_to_objects.begin();
882 i != m_sounds_to_objects.end(); i++)
884 int client_id = i->first;
885 u16 object_id = i->second;
886 ClientActiveObject *cao = m_env.getActiveObject(object_id);
889 v3f pos = cao->getPosition();
890 m_sound->updateSoundPosition(client_id, pos);
895 Handle removed remotely initiated sounds
897 m_removed_sounds_check_timer += dtime;
898 if(m_removed_sounds_check_timer >= 2.32)
900 m_removed_sounds_check_timer = 0;
901 // Find removed sounds and clear references to them
902 std::set<s32> removed_server_ids;
903 for(std::map<s32, int>::iterator
904 i = m_sounds_server_to_client.begin();
905 i != m_sounds_server_to_client.end();)
907 s32 server_id = i->first;
908 int client_id = i->second;
910 if(!m_sound->soundExists(client_id)){
911 m_sounds_server_to_client.erase(server_id);
912 m_sounds_client_to_server.erase(client_id);
913 m_sounds_to_objects.erase(client_id);
914 removed_server_ids.insert(server_id);
918 if(removed_server_ids.size() != 0)
920 std::ostringstream os(std::ios_base::binary);
921 writeU16(os, TOSERVER_REMOVED_SOUNDS);
922 writeU16(os, removed_server_ids.size());
923 for(std::set<s32>::iterator i = removed_server_ids.begin();
924 i != removed_server_ids.end(); i++)
926 std::string s = os.str();
927 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
934 bool Client::loadMedia(const std::string &data, const std::string &filename)
936 // Silly irrlicht's const-incorrectness
937 Buffer<char> data_rw(data.c_str(), data.size());
941 const char *image_ext[] = {
942 ".png", ".jpg", ".bmp", ".tga",
943 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
946 name = removeStringEnd(filename, image_ext);
949 verbosestream<<"Client: Attempting to load image "
950 <<"file \""<<filename<<"\""<<std::endl;
952 io::IFileSystem *irrfs = m_device->getFileSystem();
953 video::IVideoDriver *vdrv = m_device->getVideoDriver();
955 // Create an irrlicht memory file
956 io::IReadFile *rfile = irrfs->createMemoryReadFile(
957 *data_rw, data_rw.getSize(), "_tempreadfile");
960 video::IImage *img = vdrv->createImageFromFile(rfile);
962 errorstream<<"Client: Cannot create image from data of "
963 <<"file \""<<filename<<"\""<<std::endl;
968 m_tsrc->insertSourceImage(filename, img);
975 const char *sound_ext[] = {
976 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
977 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
980 name = removeStringEnd(filename, sound_ext);
983 verbosestream<<"Client: Attempting to load sound "
984 <<"file \""<<filename<<"\""<<std::endl;
985 m_sound->loadSoundData(name, data);
989 const char *model_ext[] = {
990 ".x", ".b3d", ".md2", ".obj",
993 name = removeStringEnd(filename, model_ext);
996 verbosestream<<"Client: Storing model into Irrlicht: "
997 <<"\""<<filename<<"\""<<std::endl;
998 scene::ISceneManager *smgr = m_device->getSceneManager();
1000 //check if mesh was already cached
1001 scene::IAnimatedMesh *mesh =
1002 smgr->getMeshCache()->getMeshByName(filename.c_str());
1005 errorstream << "Multiple models with name: " << filename.c_str() <<
1006 " found replacing previous model!" << std::endl;
1008 smgr->getMeshCache()->removeMesh(mesh);
1012 io::IFileSystem *irrfs = m_device->getFileSystem();
1013 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1014 *data_rw, data_rw.getSize(), filename.c_str());
1017 mesh = smgr->getMesh(rfile);
1018 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1023 errorstream<<"Client: Don't know how to load file \""
1024 <<filename<<"\""<<std::endl;
1028 // Virtual methods from con::PeerHandler
1029 void Client::peerAdded(con::Peer *peer)
1031 infostream<<"Client::peerAdded(): peer->id="
1032 <<peer->id<<std::endl;
1034 void Client::deletingPeer(con::Peer *peer, bool timeout)
1036 infostream<<"Client::deletingPeer(): "
1037 "Server Peer is getting deleted "
1038 <<"(timeout="<<timeout<<")"<<std::endl;
1043 u16 number of files requested
1049 void Client::request_media(const std::list<MediaRequest> &file_requests)
1051 std::ostringstream os(std::ios_base::binary);
1052 writeU16(os, TOSERVER_REQUEST_MEDIA);
1053 writeU16(os, file_requests.size());
1055 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1056 i != file_requests.end(); ++i) {
1057 os<<serializeString(i->name);
1061 std::string s = os.str();
1062 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1064 Send(0, data, true);
1065 infostream<<"Client: Sending media request list to server ("
1066 <<file_requests.size()<<" files)"<<std::endl;
1069 void Client::ReceiveAll()
1071 DSTACK(__FUNCTION_NAME);
1072 u32 start_ms = porting::getTimeMs();
1075 // Limit time even if there would be huge amounts of data to
1077 if(porting::getTimeMs() > start_ms + 100)
1082 g_profiler->graphAdd("client_received_packets", 1);
1084 catch(con::NoIncomingDataException &e)
1088 catch(con::InvalidIncomingDataException &e)
1090 infostream<<"Client::ReceiveAll(): "
1091 "InvalidIncomingDataException: what()="
1092 <<e.what()<<std::endl;
1097 void Client::Receive()
1099 DSTACK(__FUNCTION_NAME);
1100 SharedBuffer<u8> data;
1104 //TimeTaker t1("con mutex and receive", m_device);
1105 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1106 datasize = m_con.Receive(sender_peer_id, data);
1108 //TimeTaker t1("ProcessData", m_device);
1109 ProcessData(*data, datasize, sender_peer_id);
1113 sender_peer_id given to this shall be quaranteed to be a valid peer
1115 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1117 DSTACK(__FUNCTION_NAME);
1119 // Ignore packets that don't even fit a command
1122 m_packetcounter.add(60000);
1126 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1128 //infostream<<"Client: received command="<<command<<std::endl;
1129 m_packetcounter.add((u16)command);
1132 If this check is removed, be sure to change the queue
1133 system to know the ids
1135 if(sender_peer_id != PEER_ID_SERVER)
1137 infostream<<"Client::ProcessData(): Discarding data not "
1138 "coming from server: peer_id="<<sender_peer_id
1143 u8 ser_version = m_server_ser_ver;
1145 //infostream<<"Client received command="<<(int)command<<std::endl;
1147 if(command == TOCLIENT_INIT)
1152 u8 deployed = data[2];
1154 infostream<<"Client: TOCLIENT_INIT received with "
1155 "deployed="<<((int)deployed&0xff)<<std::endl;
1157 if(!ser_ver_supported(deployed))
1159 infostream<<"Client: TOCLIENT_INIT: Server sent "
1160 <<"unsupported ser_fmt_ver"<<std::endl;
1164 m_server_ser_ver = deployed;
1166 // Get player position
1167 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1168 if(datasize >= 2+1+6)
1169 playerpos_s16 = readV3S16(&data[2+1]);
1170 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1173 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1175 // Set player position
1176 Player *player = m_env.getLocalPlayer();
1177 assert(player != NULL);
1178 player->setPosition(playerpos_f);
1181 if(datasize >= 2+1+6+8)
1184 m_map_seed = readU64(&data[2+1+6]);
1185 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1188 if(datasize >= 2+1+6+8+4)
1191 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1192 infostream<<"Client: received recommended send interval "
1193 <<m_recommended_send_interval<<std::endl;
1198 SharedBuffer<u8> reply(replysize);
1199 writeU16(&reply[0], TOSERVER_INIT2);
1201 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1206 if(command == TOCLIENT_ACCESS_DENIED)
1208 // The server didn't like our password. Note, this needs
1209 // to be processed even if the serialisation format has
1210 // not been agreed yet, the same as TOCLIENT_INIT.
1211 m_access_denied = true;
1212 m_access_denied_reason = L"Unknown";
1215 std::string datastring((char*)&data[2], datasize-2);
1216 std::istringstream is(datastring, std::ios_base::binary);
1217 m_access_denied_reason = deSerializeWideString(is);
1222 if(ser_version == SER_FMT_VER_INVALID)
1224 infostream<<"Client: Server serialization"
1225 " format invalid or not initialized."
1226 " Skipping incoming command="<<command<<std::endl;
1230 // Just here to avoid putting the two if's together when
1231 // making some copypasta
1234 if(command == TOCLIENT_REMOVENODE)
1239 p.X = readS16(&data[2]);
1240 p.Y = readS16(&data[4]);
1241 p.Z = readS16(&data[6]);
1243 //TimeTaker t1("TOCLIENT_REMOVENODE");
1247 else if(command == TOCLIENT_ADDNODE)
1249 if(datasize < 8 + MapNode::serializedLength(ser_version))
1253 p.X = readS16(&data[2]);
1254 p.Y = readS16(&data[4]);
1255 p.Z = readS16(&data[6]);
1257 //TimeTaker t1("TOCLIENT_ADDNODE");
1260 n.deSerialize(&data[8], ser_version);
1264 else if(command == TOCLIENT_BLOCKDATA)
1266 // Ignore too small packet
1271 p.X = readS16(&data[2]);
1272 p.Y = readS16(&data[4]);
1273 p.Z = readS16(&data[6]);
1275 /*infostream<<"Client: Thread: BLOCKDATA for ("
1276 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1277 /*infostream<<"Client: Thread: BLOCKDATA for ("
1278 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1280 std::string datastring((char*)&data[8], datasize-8);
1281 std::istringstream istr(datastring, std::ios_base::binary);
1286 v2s16 p2d(p.X, p.Z);
1287 sector = m_env.getMap().emergeSector(p2d);
1289 assert(sector->getPos() == p2d);
1291 //TimeTaker timer("MapBlock deSerialize");
1294 block = sector->getBlockNoCreateNoEx(p.Y);
1298 Update an existing block
1300 //infostream<<"Updating"<<std::endl;
1301 block->deSerialize(istr, ser_version, false);
1302 block->deSerializeNetworkSpecific(istr);
1309 //infostream<<"Creating new"<<std::endl;
1310 block = new MapBlock(&m_env.getMap(), p, this);
1311 block->deSerialize(istr, ser_version, false);
1312 block->deSerializeNetworkSpecific(istr);
1313 sector->insertBlock(block);
1327 u32 replysize = 2+1+6;
1328 SharedBuffer<u8> reply(replysize);
1329 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1331 writeV3S16(&reply[3], p);
1333 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1337 Add it to mesh update queue and set it to be acknowledged after update.
1339 //infostream<<"Adding mesh update task for received block"<<std::endl;
1340 addUpdateMeshTaskWithEdge(p, true);
1342 else if(command == TOCLIENT_INVENTORY)
1347 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1350 //TimeTaker t2("mutex locking", m_device);
1351 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1354 //TimeTaker t3("istringstream init", m_device);
1355 std::string datastring((char*)&data[2], datasize-2);
1356 std::istringstream is(datastring, std::ios_base::binary);
1359 //m_env.printPlayers(infostream);
1361 //TimeTaker t4("player get", m_device);
1362 Player *player = m_env.getLocalPlayer();
1363 assert(player != NULL);
1366 //TimeTaker t1("inventory.deSerialize()", m_device);
1367 player->inventory.deSerialize(is);
1370 m_inventory_updated = true;
1372 delete m_inventory_from_server;
1373 m_inventory_from_server = new Inventory(player->inventory);
1374 m_inventory_from_server_age = 0.0;
1376 //infostream<<"Client got player inventory:"<<std::endl;
1377 //player->inventory.print(infostream);
1380 else if(command == TOCLIENT_TIME_OF_DAY)
1385 u16 time_of_day = readU16(&data[2]);
1386 time_of_day = time_of_day % 24000;
1387 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1388 float time_speed = 0;
1389 if(datasize >= 2 + 2 + 4){
1390 time_speed = readF1000(&data[4]);
1392 // Old message; try to approximate speed of time by ourselves
1393 float time_of_day_f = (float)time_of_day / 24000.0;
1394 float tod_diff_f = 0;
1395 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1396 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1398 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1399 m_last_time_of_day_f = time_of_day_f;
1400 float time_diff = m_time_of_day_update_timer;
1401 m_time_of_day_update_timer = 0;
1402 if(m_time_of_day_set){
1403 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1404 infostream<<"Client: Measured time_of_day speed (old format): "
1405 <<time_speed<<" tod_diff_f="<<tod_diff_f
1406 <<" time_diff="<<time_diff<<std::endl;
1410 // Update environment
1411 m_env.setTimeOfDay(time_of_day);
1412 m_env.setTimeOfDaySpeed(time_speed);
1413 m_time_of_day_set = true;
1415 u32 dr = m_env.getDayNightRatio();
1416 verbosestream<<"Client: time_of_day="<<time_of_day
1417 <<" time_speed="<<time_speed
1418 <<" dr="<<dr<<std::endl;
1420 else if(command == TOCLIENT_CHAT_MESSAGE)
1428 std::string datastring((char*)&data[2], datasize-2);
1429 std::istringstream is(datastring, std::ios_base::binary);
1432 is.read((char*)buf, 2);
1433 u16 len = readU16(buf);
1435 std::wstring message;
1436 for(u16 i=0; i<len; i++)
1438 is.read((char*)buf, 2);
1439 message += (wchar_t)readU16(buf);
1442 /*infostream<<"Client received chat message: "
1443 <<wide_to_narrow(message)<<std::endl;*/
1445 m_chat_queue.push_back(message);
1447 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1449 //if(g_settings->getBool("enable_experimental"))
1453 u16 count of removed objects
1454 for all removed objects {
1457 u16 count of added objects
1458 for all added objects {
1461 u32 initialization data length
1462 string initialization data
1467 // Get all data except the command number
1468 std::string datastring((char*)&data[2], datasize-2);
1469 // Throw them in an istringstream
1470 std::istringstream is(datastring, std::ios_base::binary);
1474 // Read removed objects
1476 u16 removed_count = readU16((u8*)buf);
1477 for(u16 i=0; i<removed_count; i++)
1480 u16 id = readU16((u8*)buf);
1483 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1484 m_env.removeActiveObject(id);
1488 // Read added objects
1490 u16 added_count = readU16((u8*)buf);
1491 for(u16 i=0; i<added_count; i++)
1494 u16 id = readU16((u8*)buf);
1496 u8 type = readU8((u8*)buf);
1497 std::string data = deSerializeLongString(is);
1500 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1501 m_env.addActiveObject(id, type, data);
1506 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1508 //if(g_settings->getBool("enable_experimental"))
1520 // Get all data except the command number
1521 std::string datastring((char*)&data[2], datasize-2);
1522 // Throw them in an istringstream
1523 std::istringstream is(datastring, std::ios_base::binary);
1525 while(is.eof() == false)
1529 u16 id = readU16((u8*)buf);
1533 u16 message_size = readU16((u8*)buf);
1534 std::string message;
1535 message.reserve(message_size);
1536 for(u16 i=0; i<message_size; i++)
1539 message.append(buf, 1);
1541 // Pass on to the environment
1543 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1544 m_env.processActiveObjectMessage(id, message);
1549 else if(command == TOCLIENT_MOVEMENT)
1551 std::string datastring((char*)&data[2], datasize-2);
1552 std::istringstream is(datastring, std::ios_base::binary);
1553 Player *player = m_env.getLocalPlayer();
1554 assert(player != NULL);
1556 player->movement_acceleration_default = readF1000(is) * BS;
1557 player->movement_acceleration_air = readF1000(is) * BS;
1558 player->movement_acceleration_fast = readF1000(is) * BS;
1559 player->movement_speed_walk = readF1000(is) * BS;
1560 player->movement_speed_crouch = readF1000(is) * BS;
1561 player->movement_speed_fast = readF1000(is) * BS;
1562 player->movement_speed_climb = readF1000(is) * BS;
1563 player->movement_speed_jump = readF1000(is) * BS;
1564 player->movement_liquid_fluidity = readF1000(is) * BS;
1565 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1566 player->movement_liquid_sink = readF1000(is) * BS;
1567 player->movement_gravity = readF1000(is) * BS;
1569 else if(command == TOCLIENT_HP)
1571 std::string datastring((char*)&data[2], datasize-2);
1572 std::istringstream is(datastring, std::ios_base::binary);
1573 Player *player = m_env.getLocalPlayer();
1574 assert(player != NULL);
1575 u8 oldhp = player->hp;
1581 // Add to ClientEvent queue
1583 event.type = CE_PLAYER_DAMAGE;
1584 event.player_damage.amount = oldhp - hp;
1585 m_client_event_queue.push_back(event);
1588 else if(command == TOCLIENT_BREATH)
1590 std::string datastring((char*)&data[2], datasize-2);
1591 std::istringstream is(datastring, std::ios_base::binary);
1592 Player *player = m_env.getLocalPlayer();
1593 assert(player != NULL);
1594 u16 breath = readU16(is);
1595 player->setBreath(breath) ;
1597 else if(command == TOCLIENT_MOVE_PLAYER)
1599 std::string datastring((char*)&data[2], datasize-2);
1600 std::istringstream is(datastring, std::ios_base::binary);
1601 Player *player = m_env.getLocalPlayer();
1602 assert(player != NULL);
1603 v3f pos = readV3F1000(is);
1604 f32 pitch = readF1000(is);
1605 f32 yaw = readF1000(is);
1606 player->setPosition(pos);
1607 /*player->setPitch(pitch);
1608 player->setYaw(yaw);*/
1610 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1611 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1617 Add to ClientEvent queue.
1618 This has to be sent to the main program because otherwise
1619 it would just force the pitch and yaw values to whatever
1620 the camera points to.
1623 event.type = CE_PLAYER_FORCE_MOVE;
1624 event.player_force_move.pitch = pitch;
1625 event.player_force_move.yaw = yaw;
1626 m_client_event_queue.push_back(event);
1628 // Ignore damage for a few seconds, so that the player doesn't
1629 // get damage from falling on ground
1630 m_ignore_damage_timer = 3.0;
1632 else if(command == TOCLIENT_PLAYERITEM)
1634 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1636 else if(command == TOCLIENT_DEATHSCREEN)
1638 std::string datastring((char*)&data[2], datasize-2);
1639 std::istringstream is(datastring, std::ios_base::binary);
1641 bool set_camera_point_target = readU8(is);
1642 v3f camera_point_target = readV3F1000(is);
1645 event.type = CE_DEATHSCREEN;
1646 event.deathscreen.set_camera_point_target = set_camera_point_target;
1647 event.deathscreen.camera_point_target_x = camera_point_target.X;
1648 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1649 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1650 m_client_event_queue.push_back(event);
1652 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1654 std::string datastring((char*)&data[2], datasize-2);
1655 std::istringstream is(datastring, std::ios_base::binary);
1657 // Mesh update thread must be stopped while
1658 // updating content definitions
1659 assert(!m_mesh_update_thread.IsRunning());
1661 int num_files = readU16(is);
1663 infostream<<"Client: Received media announcement: packet size: "
1664 <<datasize<<std::endl;
1666 std::list<MediaRequest> file_requests;
1668 for(int i=0; i<num_files; i++)
1670 //read file from cache
1671 std::string name = deSerializeString(is);
1672 std::string sha1_base64 = deSerializeString(is);
1674 // if name contains illegal characters, ignore the file
1675 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1676 errorstream<<"Client: ignoring illegal file name "
1677 <<"sent by server: \""<<name<<"\""<<std::endl;
1681 std::string sha1_raw = base64_decode(sha1_base64);
1682 std::string sha1_hex = hex_encode(sha1_raw);
1683 std::ostringstream tmp_os(std::ios_base::binary);
1684 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1685 m_media_name_sha1_map[name] = sha1_raw;
1687 // If found in cache, try to load it from there
1690 bool success = loadMedia(tmp_os.str(), name);
1692 verbosestream<<"Client: Loaded cached media: "
1693 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1696 infostream<<"Client: Failed to load cached media: "
1697 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1700 // Didn't load from cache; queue it to be requested
1701 verbosestream<<"Client: Adding file to request list: \""
1702 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1703 file_requests.push_back(MediaRequest(name));
1706 std::string remote_media = "";
1708 remote_media = deSerializeString(is);
1710 catch(SerializationError) {
1711 // not supported by server or turned off
1714 m_media_count = file_requests.size();
1715 m_media_receive_started = true;
1717 if (remote_media == "" || !USE_CURL) {
1718 request_media(file_requests);
1721 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1722 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1723 i != file_requests.end(); ++i) {
1724 (*cur)->m_file_requests.push_back(*i);
1726 if (cur == m_media_fetch_threads.end())
1727 cur = m_media_fetch_threads.begin();
1729 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1730 i != m_media_fetch_threads.end(); ++i) {
1731 (*i)->m_remote_url = remote_media;
1736 // notify server we received everything
1737 std::ostringstream os(std::ios_base::binary);
1738 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1739 std::string s = os.str();
1740 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1742 Send(0, data, true);
1745 event.type = CE_TEXTURES_UPDATED;
1746 m_client_event_queue.push_back(event);
1748 else if(command == TOCLIENT_MEDIA)
1750 std::string datastring((char*)&data[2], datasize-2);
1751 std::istringstream is(datastring, std::ios_base::binary);
1755 u16 total number of file bunches
1756 u16 index of this bunch
1757 u32 number of files in this bunch
1765 int num_bunches = readU16(is);
1766 int bunch_i = readU16(is);
1767 u32 num_files = readU32(is);
1768 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1769 <<num_bunches<<" files="<<num_files
1770 <<" size="<<datasize<<std::endl;
1772 // Check total and received media count
1773 assert(m_media_received_count <= m_media_count);
1774 if (num_files > m_media_count - m_media_received_count) {
1775 errorstream<<"Client: Received more files than requested:"
1776 <<" total count="<<m_media_count
1777 <<" total received="<<m_media_received_count
1778 <<" bunch "<<bunch_i<<"/"<<num_bunches
1779 <<" files="<<num_files
1780 <<" size="<<datasize<<std::endl;
1781 num_files = m_media_count - m_media_received_count;
1786 // Mesh update thread must be stopped while
1787 // updating content definitions
1788 assert(!m_mesh_update_thread.IsRunning());
1790 for(u32 i=0; i<num_files; i++){
1791 assert(m_media_received_count < m_media_count);
1792 m_media_received_count++;
1793 std::string name = deSerializeString(is);
1794 std::string data = deSerializeLongString(is);
1796 // if name contains illegal characters, ignore the file
1797 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1798 errorstream<<"Client: ignoring illegal file name "
1799 <<"sent by server: \""<<name<<"\""<<std::endl;
1803 bool success = loadMedia(data, name);
1805 verbosestream<<"Client: Loaded received media: "
1806 <<"\""<<name<<"\". Caching."<<std::endl;
1808 infostream<<"Client: Failed to load received media: "
1809 <<"\""<<name<<"\". Not caching."<<std::endl;
1813 bool did = fs::CreateAllDirs(getMediaCacheDir());
1815 errorstream<<"Could not create media cache directory"
1820 std::map<std::string, std::string>::iterator n;
1821 n = m_media_name_sha1_map.find(name);
1822 if(n == m_media_name_sha1_map.end())
1823 errorstream<<"The server sent a file that has not "
1824 <<"been announced."<<std::endl;
1826 m_media_cache.update_sha1(data);
1831 event.type = CE_TEXTURES_UPDATED;
1832 m_client_event_queue.push_back(event);
1834 else if(command == TOCLIENT_TOOLDEF)
1836 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1838 else if(command == TOCLIENT_NODEDEF)
1840 infostream<<"Client: Received node definitions: packet size: "
1841 <<datasize<<std::endl;
1843 // Mesh update thread must be stopped while
1844 // updating content definitions
1845 assert(!m_mesh_update_thread.IsRunning());
1847 // Decompress node definitions
1848 std::string datastring((char*)&data[2], datasize-2);
1849 std::istringstream is(datastring, std::ios_base::binary);
1850 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1851 std::ostringstream tmp_os;
1852 decompressZlib(tmp_is, tmp_os);
1854 // Deserialize node definitions
1855 std::istringstream tmp_is2(tmp_os.str());
1856 m_nodedef->deSerialize(tmp_is2);
1857 m_nodedef_received = true;
1859 else if(command == TOCLIENT_CRAFTITEMDEF)
1861 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1863 else if(command == TOCLIENT_ITEMDEF)
1865 infostream<<"Client: Received item definitions: packet size: "
1866 <<datasize<<std::endl;
1868 // Mesh update thread must be stopped while
1869 // updating content definitions
1870 assert(!m_mesh_update_thread.IsRunning());
1872 // Decompress item definitions
1873 std::string datastring((char*)&data[2], datasize-2);
1874 std::istringstream is(datastring, std::ios_base::binary);
1875 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1876 std::ostringstream tmp_os;
1877 decompressZlib(tmp_is, tmp_os);
1879 // Deserialize node definitions
1880 std::istringstream tmp_is2(tmp_os.str());
1881 m_itemdef->deSerialize(tmp_is2);
1882 m_itemdef_received = true;
1884 else if(command == TOCLIENT_PLAY_SOUND)
1886 std::string datastring((char*)&data[2], datasize-2);
1887 std::istringstream is(datastring, std::ios_base::binary);
1889 s32 server_id = readS32(is);
1890 std::string name = deSerializeString(is);
1891 float gain = readF1000(is);
1892 int type = readU8(is); // 0=local, 1=positional, 2=object
1893 v3f pos = readV3F1000(is);
1894 u16 object_id = readU16(is);
1895 bool loop = readU8(is);
1900 client_id = m_sound->playSound(name, loop, gain);
1902 case 1: // positional
1903 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1906 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1908 pos = cao->getPosition();
1909 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1910 // TODO: Set up sound to move with object
1915 if(client_id != -1){
1916 m_sounds_server_to_client[server_id] = client_id;
1917 m_sounds_client_to_server[client_id] = server_id;
1919 m_sounds_to_objects[client_id] = object_id;
1922 else if(command == TOCLIENT_STOP_SOUND)
1924 std::string datastring((char*)&data[2], datasize-2);
1925 std::istringstream is(datastring, std::ios_base::binary);
1927 s32 server_id = readS32(is);
1928 std::map<s32, int>::iterator i =
1929 m_sounds_server_to_client.find(server_id);
1930 if(i != m_sounds_server_to_client.end()){
1931 int client_id = i->second;
1932 m_sound->stopSound(client_id);
1935 else if(command == TOCLIENT_PRIVILEGES)
1937 std::string datastring((char*)&data[2], datasize-2);
1938 std::istringstream is(datastring, std::ios_base::binary);
1940 m_privileges.clear();
1941 infostream<<"Client: Privileges updated: ";
1942 u16 num_privileges = readU16(is);
1943 for(u16 i=0; i<num_privileges; i++){
1944 std::string priv = deSerializeString(is);
1945 m_privileges.insert(priv);
1946 infostream<<priv<<" ";
1948 infostream<<std::endl;
1950 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1952 std::string datastring((char*)&data[2], datasize-2);
1953 std::istringstream is(datastring, std::ios_base::binary);
1955 // Store formspec in LocalPlayer
1956 Player *player = m_env.getLocalPlayer();
1957 assert(player != NULL);
1958 player->inventory_formspec = deSerializeLongString(is);
1960 else if(command == TOCLIENT_DETACHED_INVENTORY)
1962 std::string datastring((char*)&data[2], datasize-2);
1963 std::istringstream is(datastring, std::ios_base::binary);
1965 std::string name = deSerializeString(is);
1967 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1969 Inventory *inv = NULL;
1970 if(m_detached_inventories.count(name) > 0)
1971 inv = m_detached_inventories[name];
1973 inv = new Inventory(m_itemdef);
1974 m_detached_inventories[name] = inv;
1976 inv->deSerialize(is);
1978 else if(command == TOCLIENT_SHOW_FORMSPEC)
1980 std::string datastring((char*)&data[2], datasize-2);
1981 std::istringstream is(datastring, std::ios_base::binary);
1983 std::string formspec = deSerializeLongString(is);
1984 std::string formname = deSerializeString(is);
1987 event.type = CE_SHOW_FORMSPEC;
1988 // pointer is required as event is a struct only!
1989 // adding a std:string to a struct isn't possible
1990 event.show_formspec.formspec = new std::string(formspec);
1991 event.show_formspec.formname = new std::string(formname);
1992 m_client_event_queue.push_back(event);
1994 else if(command == TOCLIENT_SPAWN_PARTICLE)
1996 std::string datastring((char*)&data[2], datasize-2);
1997 std::istringstream is(datastring, std::ios_base::binary);
1999 v3f pos = readV3F1000(is);
2000 v3f vel = readV3F1000(is);
2001 v3f acc = readV3F1000(is);
2002 float expirationtime = readF1000(is);
2003 float size = readF1000(is);
2004 bool collisiondetection = readU8(is);
2005 std::string texture = deSerializeLongString(is);
2008 event.type = CE_SPAWN_PARTICLE;
2009 event.spawn_particle.pos = new v3f (pos);
2010 event.spawn_particle.vel = new v3f (vel);
2011 event.spawn_particle.acc = new v3f (acc);
2013 event.spawn_particle.expirationtime = expirationtime;
2014 event.spawn_particle.size = size;
2015 event.spawn_particle.collisiondetection =
2017 event.spawn_particle.texture = new std::string(texture);
2019 m_client_event_queue.push_back(event);
2021 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2023 std::string datastring((char*)&data[2], datasize-2);
2024 std::istringstream is(datastring, std::ios_base::binary);
2026 u16 amount = readU16(is);
2027 float spawntime = readF1000(is);
2028 v3f minpos = readV3F1000(is);
2029 v3f maxpos = readV3F1000(is);
2030 v3f minvel = readV3F1000(is);
2031 v3f maxvel = readV3F1000(is);
2032 v3f minacc = readV3F1000(is);
2033 v3f maxacc = readV3F1000(is);
2034 float minexptime = readF1000(is);
2035 float maxexptime = readF1000(is);
2036 float minsize = readF1000(is);
2037 float maxsize = readF1000(is);
2038 bool collisiondetection = readU8(is);
2039 std::string texture = deSerializeLongString(is);
2040 u32 id = readU32(is);
2043 event.type = CE_ADD_PARTICLESPAWNER;
2044 event.add_particlespawner.amount = amount;
2045 event.add_particlespawner.spawntime = spawntime;
2047 event.add_particlespawner.minpos = new v3f (minpos);
2048 event.add_particlespawner.maxpos = new v3f (maxpos);
2049 event.add_particlespawner.minvel = new v3f (minvel);
2050 event.add_particlespawner.maxvel = new v3f (maxvel);
2051 event.add_particlespawner.minacc = new v3f (minacc);
2052 event.add_particlespawner.maxacc = new v3f (maxacc);
2054 event.add_particlespawner.minexptime = minexptime;
2055 event.add_particlespawner.maxexptime = maxexptime;
2056 event.add_particlespawner.minsize = minsize;
2057 event.add_particlespawner.maxsize = maxsize;
2058 event.add_particlespawner.collisiondetection = collisiondetection;
2059 event.add_particlespawner.texture = new std::string(texture);
2060 event.add_particlespawner.id = id;
2062 m_client_event_queue.push_back(event);
2064 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2066 std::string datastring((char*)&data[2], datasize-2);
2067 std::istringstream is(datastring, std::ios_base::binary);
2069 u32 id = readU16(is);
2072 event.type = CE_DELETE_PARTICLESPAWNER;
2073 event.delete_particlespawner.id = id;
2075 m_client_event_queue.push_back(event);
2077 else if(command == TOCLIENT_HUDADD)
2079 std::string datastring((char *)&data[2], datasize - 2);
2080 std::istringstream is(datastring, std::ios_base::binary);
2082 u32 id = readU32(is);
2083 u8 type = readU8(is);
2084 v2f pos = readV2F1000(is);
2085 std::string name = deSerializeString(is);
2086 v2f scale = readV2F1000(is);
2087 std::string text = deSerializeString(is);
2088 u32 number = readU32(is);
2089 u32 item = readU32(is);
2090 u32 dir = readU32(is);
2091 v2f align = readV2F1000(is);
2092 v2f offset = readV2F1000(is);
2095 event.type = CE_HUDADD;
2096 event.hudadd.id = id;
2097 event.hudadd.type = type;
2098 event.hudadd.pos = new v2f(pos);
2099 event.hudadd.name = new std::string(name);
2100 event.hudadd.scale = new v2f(scale);
2101 event.hudadd.text = new std::string(text);
2102 event.hudadd.number = number;
2103 event.hudadd.item = item;
2104 event.hudadd.dir = dir;
2105 event.hudadd.align = new v2f(align);
2106 event.hudadd.offset = new v2f(offset);
2107 m_client_event_queue.push_back(event);
2109 else if(command == TOCLIENT_HUDRM)
2111 std::string datastring((char *)&data[2], datasize - 2);
2112 std::istringstream is(datastring, std::ios_base::binary);
2114 u32 id = readU32(is);
2117 event.type = CE_HUDRM;
2118 event.hudrm.id = id;
2119 m_client_event_queue.push_back(event);
2121 else if(command == TOCLIENT_HUDCHANGE)
2127 std::string datastring((char *)&data[2], datasize - 2);
2128 std::istringstream is(datastring, std::ios_base::binary);
2130 u32 id = readU32(is);
2131 u8 stat = (HudElementStat)readU8(is);
2133 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2134 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2135 v2fdata = readV2F1000(is);
2136 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2137 sdata = deSerializeString(is);
2139 intdata = readU32(is);
2142 event.type = CE_HUDCHANGE;
2143 event.hudchange.id = id;
2144 event.hudchange.stat = (HudElementStat)stat;
2145 event.hudchange.v2fdata = new v2f(v2fdata);
2146 event.hudchange.sdata = new std::string(sdata);
2147 event.hudchange.data = intdata;
2148 m_client_event_queue.push_back(event);
2150 else if(command == TOCLIENT_HUD_SET_FLAGS)
2152 std::string datastring((char *)&data[2], datasize - 2);
2153 std::istringstream is(datastring, std::ios_base::binary);
2155 Player *player = m_env.getLocalPlayer();
2156 assert(player != NULL);
2158 u32 flags = readU32(is);
2159 u32 mask = readU32(is);
2161 player->hud_flags &= ~mask;
2162 player->hud_flags |= flags;
2164 else if(command == TOCLIENT_HUD_SET_PARAM)
2166 std::string datastring((char *)&data[2], datasize - 2);
2167 std::istringstream is(datastring, std::ios_base::binary);
2169 Player *player = m_env.getLocalPlayer();
2170 assert(player != NULL);
2172 u16 param = readU16(is);
2173 std::string value = deSerializeString(is);
2175 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2176 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2177 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2178 player->hud_hotbar_itemcount = hotbar_itemcount;
2183 infostream<<"Client: Ignoring unknown command "
2184 <<command<<std::endl;
2188 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2190 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2191 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2194 void Client::interact(u8 action, const PointedThing& pointed)
2196 if(connectedAndInitialized() == false){
2197 infostream<<"Client::interact() "
2198 "cancelled (not connected)"
2203 std::ostringstream os(std::ios_base::binary);
2209 [5] u32 length of the next item
2210 [9] serialized PointedThing
2212 0: start digging (from undersurface) or use
2213 1: stop digging (all parameters ignored)
2214 2: digging completed
2215 3: place block or item (to abovesurface)
2218 writeU16(os, TOSERVER_INTERACT);
2219 writeU8(os, action);
2220 writeU16(os, getPlayerItem());
2221 std::ostringstream tmp_os(std::ios::binary);
2222 pointed.serialize(tmp_os);
2223 os<<serializeLongString(tmp_os.str());
2225 std::string s = os.str();
2226 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2229 Send(0, data, true);
2232 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2233 const std::map<std::string, std::string> &fields)
2235 std::ostringstream os(std::ios_base::binary);
2237 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2239 os<<serializeString(formname);
2240 writeU16(os, fields.size());
2241 for(std::map<std::string, std::string>::const_iterator
2242 i = fields.begin(); i != fields.end(); i++){
2243 const std::string &name = i->first;
2244 const std::string &value = i->second;
2245 os<<serializeString(name);
2246 os<<serializeLongString(value);
2250 std::string s = os.str();
2251 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2253 Send(0, data, true);
2256 void Client::sendInventoryFields(const std::string &formname,
2257 const std::map<std::string, std::string> &fields)
2259 std::ostringstream os(std::ios_base::binary);
2261 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2262 os<<serializeString(formname);
2263 writeU16(os, fields.size());
2264 for(std::map<std::string, std::string>::const_iterator
2265 i = fields.begin(); i != fields.end(); i++){
2266 const std::string &name = i->first;
2267 const std::string &value = i->second;
2268 os<<serializeString(name);
2269 os<<serializeLongString(value);
2273 std::string s = os.str();
2274 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2276 Send(0, data, true);
2279 void Client::sendInventoryAction(InventoryAction *a)
2281 std::ostringstream os(std::ios_base::binary);
2285 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2286 os.write((char*)buf, 2);
2291 std::string s = os.str();
2292 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2294 Send(0, data, true);
2297 void Client::sendChatMessage(const std::wstring &message)
2299 std::ostringstream os(std::ios_base::binary);
2303 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2304 os.write((char*)buf, 2);
2307 writeU16(buf, message.size());
2308 os.write((char*)buf, 2);
2311 for(u32 i=0; i<message.size(); i++)
2315 os.write((char*)buf, 2);
2319 std::string s = os.str();
2320 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2322 Send(0, data, true);
2325 void Client::sendChangePassword(const std::wstring oldpassword,
2326 const std::wstring newpassword)
2328 Player *player = m_env.getLocalPlayer();
2332 std::string playername = player->getName();
2333 std::string oldpwd = translatePassword(playername, oldpassword);
2334 std::string newpwd = translatePassword(playername, newpassword);
2336 std::ostringstream os(std::ios_base::binary);
2337 u8 buf[2+PASSWORD_SIZE*2];
2339 [0] u16 TOSERVER_PASSWORD
2340 [2] u8[28] old password
2341 [30] u8[28] new password
2344 writeU16(buf, TOSERVER_PASSWORD);
2345 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2347 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2348 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2350 buf[2+PASSWORD_SIZE-1] = 0;
2351 buf[30+PASSWORD_SIZE-1] = 0;
2352 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2355 std::string s = os.str();
2356 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2358 Send(0, data, true);
2362 void Client::sendDamage(u8 damage)
2364 DSTACK(__FUNCTION_NAME);
2365 std::ostringstream os(std::ios_base::binary);
2367 writeU16(os, TOSERVER_DAMAGE);
2368 writeU8(os, damage);
2371 std::string s = os.str();
2372 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2374 Send(0, data, true);
2377 void Client::sendBreath(u16 breath)
2379 DSTACK(__FUNCTION_NAME);
2380 std::ostringstream os(std::ios_base::binary);
2382 writeU16(os, TOSERVER_BREATH);
2383 writeU16(os, breath);
2385 std::string s = os.str();
2386 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2388 Send(0, data, true);
2391 void Client::sendRespawn()
2393 DSTACK(__FUNCTION_NAME);
2394 std::ostringstream os(std::ios_base::binary);
2396 writeU16(os, TOSERVER_RESPAWN);
2399 std::string s = os.str();
2400 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2402 Send(0, data, true);
2405 void Client::sendPlayerPos()
2407 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2409 LocalPlayer *myplayer = m_env.getLocalPlayer();
2410 if(myplayer == NULL)
2413 // Save bandwidth by only updating position when something changed
2414 if(myplayer->last_position == myplayer->getPosition() &&
2415 myplayer->last_speed == myplayer->getSpeed() &&
2416 myplayer->last_pitch == myplayer->getPitch() &&
2417 myplayer->last_yaw == myplayer->getYaw() &&
2418 myplayer->last_keyPressed == myplayer->keyPressed)
2421 myplayer->last_position = myplayer->getPosition();
2422 myplayer->last_speed = myplayer->getSpeed();
2423 myplayer->last_pitch = myplayer->getPitch();
2424 myplayer->last_yaw = myplayer->getYaw();
2425 myplayer->last_keyPressed = myplayer->keyPressed;
2429 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2430 our_peer_id = m_con.GetPeerID();
2433 // Set peer id if not set already
2434 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2435 myplayer->peer_id = our_peer_id;
2436 // Check that an existing peer_id is the same as the connection's
2437 assert(myplayer->peer_id == our_peer_id);
2439 v3f pf = myplayer->getPosition();
2440 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2441 v3f sf = myplayer->getSpeed();
2442 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2443 s32 pitch = myplayer->getPitch() * 100;
2444 s32 yaw = myplayer->getYaw() * 100;
2445 u32 keyPressed=myplayer->keyPressed;
2449 [2] v3s32 position*100
2450 [2+12] v3s32 speed*100
2451 [2+12+12] s32 pitch*100
2452 [2+12+12+4] s32 yaw*100
2453 [2+12+12+4+4] u32 keyPressed
2455 SharedBuffer<u8> data(2+12+12+4+4+4);
2456 writeU16(&data[0], TOSERVER_PLAYERPOS);
2457 writeV3S32(&data[2], position);
2458 writeV3S32(&data[2+12], speed);
2459 writeS32(&data[2+12+12], pitch);
2460 writeS32(&data[2+12+12+4], yaw);
2461 writeU32(&data[2+12+12+4+4], keyPressed);
2462 // Send as unreliable
2463 Send(0, data, false);
2466 void Client::sendPlayerItem(u16 item)
2468 Player *myplayer = m_env.getLocalPlayer();
2469 if(myplayer == NULL)
2472 u16 our_peer_id = m_con.GetPeerID();
2474 // Set peer id if not set already
2475 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2476 myplayer->peer_id = our_peer_id;
2477 // Check that an existing peer_id is the same as the connection's
2478 assert(myplayer->peer_id == our_peer_id);
2480 SharedBuffer<u8> data(2+2);
2481 writeU16(&data[0], TOSERVER_PLAYERITEM);
2482 writeU16(&data[2], item);
2485 Send(0, data, true);
2488 void Client::removeNode(v3s16 p)
2490 std::map<v3s16, MapBlock*> modified_blocks;
2494 //TimeTaker t("removeNodeAndUpdate", m_device);
2495 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2497 catch(InvalidPositionException &e)
2501 // add urgent task to update the modified node
2502 addUpdateMeshTaskForNode(p, false, true);
2504 for(std::map<v3s16, MapBlock * >::iterator
2505 i = modified_blocks.begin();
2506 i != modified_blocks.end(); ++i)
2508 addUpdateMeshTaskWithEdge(i->first);
2512 void Client::addNode(v3s16 p, MapNode n)
2514 TimeTaker timer1("Client::addNode()");
2516 std::map<v3s16, MapBlock*> modified_blocks;
2520 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2521 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2523 catch(InvalidPositionException &e)
2526 for(std::map<v3s16, MapBlock * >::iterator
2527 i = modified_blocks.begin();
2528 i != modified_blocks.end(); ++i)
2530 addUpdateMeshTaskWithEdge(i->first);
2534 void Client::setPlayerControl(PlayerControl &control)
2536 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2537 LocalPlayer *player = m_env.getLocalPlayer();
2538 assert(player != NULL);
2539 player->control = control;
2542 void Client::selectPlayerItem(u16 item)
2544 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2545 m_playeritem = item;
2546 m_inventory_updated = true;
2547 sendPlayerItem(item);
2550 // Returns true if the inventory of the local player has been
2551 // updated from the server. If it is true, it is set to false.
2552 bool Client::getLocalInventoryUpdated()
2554 // m_inventory_updated is behind envlock
2555 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2556 bool updated = m_inventory_updated;
2557 m_inventory_updated = false;
2561 // Copies the inventory of the local player to parameter
2562 void Client::getLocalInventory(Inventory &dst)
2564 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2565 Player *player = m_env.getLocalPlayer();
2566 assert(player != NULL);
2567 dst = player->inventory;
2570 Inventory* Client::getInventory(const InventoryLocation &loc)
2573 case InventoryLocation::UNDEFINED:
2576 case InventoryLocation::CURRENT_PLAYER:
2578 Player *player = m_env.getLocalPlayer();
2579 assert(player != NULL);
2580 return &player->inventory;
2583 case InventoryLocation::PLAYER:
2585 Player *player = m_env.getPlayer(loc.name.c_str());
2588 return &player->inventory;
2591 case InventoryLocation::NODEMETA:
2593 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2596 return meta->getInventory();
2599 case InventoryLocation::DETACHED:
2601 if(m_detached_inventories.count(loc.name) == 0)
2603 return m_detached_inventories[loc.name];
2611 void Client::inventoryAction(InventoryAction *a)
2614 Send it to the server
2616 sendInventoryAction(a);
2619 Predict some local inventory changes
2621 a->clientApply(this, this);
2627 ClientActiveObject * Client::getSelectedActiveObject(
2629 v3f from_pos_f_on_map,
2630 core::line3d<f32> shootline_on_map
2633 std::vector<DistanceSortedActiveObject> objects;
2635 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2637 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2640 // After this, the closest object is the first in the array.
2641 std::sort(objects.begin(), objects.end());
2643 for(u32 i=0; i<objects.size(); i++)
2645 ClientActiveObject *obj = objects[i].obj;
2647 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2648 if(selection_box == NULL)
2651 v3f pos = obj->getPosition();
2653 core::aabbox3d<f32> offsetted_box(
2654 selection_box->MinEdge + pos,
2655 selection_box->MaxEdge + pos
2658 if(offsetted_box.intersectsWithLine(shootline_on_map))
2660 //infostream<<"Returning selected object"<<std::endl;
2665 //infostream<<"No object selected; returning NULL."<<std::endl;
2669 void Client::printDebugInfo(std::ostream &os)
2671 //JMutexAutoLock lock1(m_fetchblock_mutex);
2672 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2674 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2675 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2676 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2680 std::list<std::string> Client::getConnectedPlayerNames()
2682 return m_env.getPlayerNames();
2685 float Client::getAnimationTime()
2687 return m_animation_time;
2690 int Client::getCrackLevel()
2692 return m_crack_level;
2695 void Client::setCrack(int level, v3s16 pos)
2697 int old_crack_level = m_crack_level;
2698 v3s16 old_crack_pos = m_crack_pos;
2700 m_crack_level = level;
2703 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2706 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2708 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2711 addUpdateMeshTaskForNode(pos, false, true);
2717 Player *player = m_env.getLocalPlayer();
2718 assert(player != NULL);
2722 u16 Client::getBreath()
2724 Player *player = m_env.getLocalPlayer();
2725 assert(player != NULL);
2726 return player->getBreath();
2729 bool Client::getChatMessage(std::wstring &message)
2731 if(m_chat_queue.size() == 0)
2733 message = m_chat_queue.pop_front();
2737 void Client::typeChatMessage(const std::wstring &message)
2739 // Discard empty line
2744 sendChatMessage(message);
2747 if (message[0] == L'/')
2749 m_chat_queue.push_back(
2750 (std::wstring)L"issued command: "+message);
2754 LocalPlayer *player = m_env.getLocalPlayer();
2755 assert(player != NULL);
2756 std::wstring name = narrow_to_wide(player->getName());
2757 m_chat_queue.push_back(
2758 (std::wstring)L"<"+name+L"> "+message);
2762 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2764 /*infostream<<"Client::addUpdateMeshTask(): "
2765 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2766 <<" ack_to_server="<<ack_to_server
2767 <<" urgent="<<urgent
2770 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2775 Create a task to update the mesh of the block
2778 MeshMakeData *data = new MeshMakeData(this);
2781 //TimeTaker timer("data fill");
2783 // Debug: 1-6ms, avg=2ms
2785 data->setCrack(m_crack_level, m_crack_pos);
2786 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2790 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2792 // Add task to queue
2793 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2795 /*infostream<<"Mesh update input queue size is "
2796 <<m_mesh_update_thread.m_queue_in.size()
2800 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2804 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2805 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2810 v3s16 p = blockpos + v3s16(0,0,0);
2811 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2812 addUpdateMeshTask(p, ack_to_server, urgent);
2814 catch(InvalidPositionException &e){}
2816 for (int i=0;i<6;i++)
2819 v3s16 p = blockpos + g_6dirs[i];
2820 addUpdateMeshTask(p, false, urgent);
2822 catch(InvalidPositionException &e){}
2826 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2830 infostream<<"Client::addUpdateMeshTaskForNode(): "
2831 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2835 v3s16 blockpos = getNodeBlockPos(nodepos);
2836 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2839 v3s16 p = blockpos + v3s16(0,0,0);
2840 addUpdateMeshTask(p, ack_to_server, urgent);
2842 catch(InvalidPositionException &e){}
2844 if(nodepos.X == blockpos_relative.X){
2846 v3s16 p = blockpos + v3s16(-1,0,0);
2847 addUpdateMeshTask(p, false, urgent);
2849 catch(InvalidPositionException &e){}
2851 if(nodepos.Y == blockpos_relative.Y){
2853 v3s16 p = blockpos + v3s16(0,-1,0);
2854 addUpdateMeshTask(p, false, urgent);
2856 catch(InvalidPositionException &e){}
2858 if(nodepos.Z == blockpos_relative.Z){
2860 v3s16 p = blockpos + v3s16(0,0,-1);
2861 addUpdateMeshTask(p, false, urgent);
2863 catch(InvalidPositionException &e){}
2867 ClientEvent Client::getClientEvent()
2869 if(m_client_event_queue.size() == 0)
2872 event.type = CE_NONE;
2875 return m_client_event_queue.pop_front();
2878 void draw_load_screen(const std::wstring &text,
2879 IrrlichtDevice* device, gui::IGUIFont* font,
2880 float dtime=0 ,int percent=0, bool clouds=true);
2881 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2883 infostream<<"Client::afterContentReceived() started"<<std::endl;
2884 assert(m_itemdef_received);
2885 assert(m_nodedef_received);
2886 assert(texturesReceived());
2888 // remove the information about which checksum each texture
2890 m_media_name_sha1_map.clear();
2892 // Rebuild inherited images and recreate textures
2893 infostream<<"- Rebuilding images and textures"<<std::endl;
2894 m_tsrc->rebuildImagesAndTextures();
2897 infostream<<"- Rebuilding shaders"<<std::endl;
2898 m_shsrc->rebuildShaders();
2900 // Update node aliases
2901 infostream<<"- Updating node aliases"<<std::endl;
2902 m_nodedef->updateAliases(m_itemdef);
2904 // Update node textures
2905 infostream<<"- Updating node textures"<<std::endl;
2906 m_nodedef->updateTextures(m_tsrc);
2908 // Preload item textures and meshes if configured to
2909 if(g_settings->getBool("preload_item_visuals"))
2911 verbosestream<<"Updating item textures and meshes"<<std::endl;
2912 wchar_t* text = wgettext("Item textures...");
2913 draw_load_screen(text,device,font,0,0);
2914 std::set<std::string> names = m_itemdef->getAll();
2915 size_t size = names.size();
2918 for(std::set<std::string>::const_iterator
2919 i = names.begin(); i != names.end(); ++i){
2920 // Asking for these caches the result
2921 m_itemdef->getInventoryTexture(*i, this);
2922 m_itemdef->getWieldMesh(*i, this);
2924 percent = count*100/size;
2925 if (count%50 == 0) // only update every 50 item
2926 draw_load_screen(text,device,font,0,percent);
2931 // Start mesh update thread after setting up content definitions
2932 infostream<<"- Starting mesh update thread"<<std::endl;
2933 m_mesh_update_thread.Start();
2935 infostream<<"Client::afterContentReceived() done"<<std::endl;
2938 float Client::getRTT(void)
2941 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2942 } catch(con::PeerNotFoundException &e){
2947 // IGameDef interface
2949 IItemDefManager* Client::getItemDefManager()
2953 INodeDefManager* Client::getNodeDefManager()
2957 ICraftDefManager* Client::getCraftDefManager()
2960 //return m_craftdef;
2962 ITextureSource* Client::getTextureSource()
2966 IShaderSource* Client::getShaderSource()
2970 u16 Client::allocateUnknownNodeId(const std::string &name)
2972 errorstream<<"Client::allocateUnknownNodeId(): "
2973 <<"Client cannot allocate node IDs"<<std::endl;
2975 return CONTENT_IGNORE;
2977 ISoundManager* Client::getSoundManager()
2981 MtEventManager* Client::getEventManager()