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()
88 MeshUpdateQueue::~MeshUpdateQueue()
90 JMutexAutoLock lock(m_mutex);
92 for(std::vector<QueuedMeshUpdate*>::iterator
94 i != m_queue.end(); i++)
96 QueuedMeshUpdate *q = *i;
102 peer_id=0 adds with nobody to send to
104 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
106 DSTACK(__FUNCTION_NAME);
110 JMutexAutoLock lock(m_mutex);
116 Find if block is already in queue.
117 If it is, update the data and quit.
119 for(std::vector<QueuedMeshUpdate*>::iterator
121 i != m_queue.end(); i++)
123 QueuedMeshUpdate *q = *i;
129 if(ack_block_to_server)
130 q->ack_block_to_server = true;
138 QueuedMeshUpdate *q = new QueuedMeshUpdate;
141 q->ack_block_to_server = ack_block_to_server;
142 m_queue.push_back(q);
145 // Returned pointer must be deleted
146 // Returns NULL if queue is empty
147 QueuedMeshUpdate * MeshUpdateQueue::pop()
149 JMutexAutoLock lock(m_mutex);
151 bool must_be_urgent = !m_urgents.empty();
152 for(std::vector<QueuedMeshUpdate*>::iterator
154 i != m_queue.end(); i++)
156 QueuedMeshUpdate *q = *i;
157 if(must_be_urgent && m_urgents.count(q->p) == 0)
160 m_urgents.erase(q->p);
170 void * MeshUpdateThread::Thread()
174 log_register_thread("MeshUpdateThread");
176 DSTACK(__FUNCTION_NAME);
178 BEGIN_DEBUG_EXCEPTION_HANDLER
182 /*// Wait for output queue to flush.
183 // Allow 2 in queue, this makes less frametime jitter.
184 // Umm actually, there is no much difference
185 if(m_queue_out.size() >= 2)
191 QueuedMeshUpdate *q = m_queue_in.pop();
198 ScopeProfiler sp(g_profiler, "Client: Mesh making");
200 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
201 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
210 r.ack_block_to_server = q->ack_block_to_server;
212 /*infostream<<"MeshUpdateThread: Processed "
213 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
216 m_queue_out.push_back(r);
221 END_DEBUG_EXCEPTION_HANDLER(errorstream)
226 void * MediaFetchThread::Thread()
230 log_register_thread("MediaFetchThread");
232 DSTACK(__FUNCTION_NAME);
234 BEGIN_DEBUG_EXCEPTION_HANDLER
239 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
240 i != m_file_requests.end(); ++i) {
241 curl = curl_easy_init();
243 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
244 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
245 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
246 std::ostringstream stream;
247 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
248 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
249 curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
250 res = curl_easy_perform(curl);
251 if (res == CURLE_OK) {
252 std::string data = stream.str();
253 m_file_data.push_back(make_pair(i->name, data));
255 m_failed.push_back(*i);
256 infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl;
258 curl_easy_cleanup(curl);
262 END_DEBUG_EXCEPTION_HANDLER(errorstream)
268 IrrlichtDevice *device,
269 const char *playername,
270 std::string password,
271 MapDrawControl &control,
272 IWritableTextureSource *tsrc,
273 IWritableShaderSource *shsrc,
274 IWritableItemDefManager *itemdef,
275 IWritableNodeDefManager *nodedef,
276 ISoundManager *sound,
277 MtEventManager *event,
286 m_mesh_update_thread(this),
288 new ClientMap(this, this, control,
289 device->getSceneManager()->getRootSceneNode(),
290 device->getSceneManager(), 666),
291 device->getSceneManager(),
294 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
296 m_server_ser_ver(SER_FMT_VER_INVALID),
298 m_inventory_updated(false),
299 m_inventory_from_server(NULL),
300 m_inventory_from_server_age(0.0),
305 m_password(password),
306 m_access_denied(false),
307 m_media_cache(getMediaCacheDir()),
308 m_media_receive_started(false),
310 m_media_received_count(0),
311 m_itemdef_received(false),
312 m_nodedef_received(false),
313 m_time_of_day_set(false),
314 m_last_time_of_day_f(-1),
315 m_time_of_day_update_timer(0),
316 m_recommended_send_interval(0.1),
317 m_removed_sounds_check_timer(0)
319 m_packetcounter_timer = 0.0;
320 //m_delete_unused_sectors_timer = 0.0;
321 m_connection_reinit_timer = 0.0;
322 m_avg_rtt_timer = 0.0;
323 m_playerpos_send_timer = 0.0;
324 m_ignore_damage_timer = 0.0;
330 Player *player = new LocalPlayer(this);
332 player->updateName(playername);
334 m_env.addPlayer(player);
337 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
338 m_media_fetch_threads.push_back(new MediaFetchThread(this));
344 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
348 m_mesh_update_thread.setRun(false);
349 while(m_mesh_update_thread.IsRunning())
351 while(!m_mesh_update_thread.m_queue_out.empty()) {
352 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
357 delete m_inventory_from_server;
359 // Delete detached inventories
361 for(std::map<std::string, Inventory*>::iterator
362 i = m_detached_inventories.begin();
363 i != m_detached_inventories.end(); i++){
368 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
369 i != m_media_fetch_threads.end(); ++i)
372 // cleanup 3d model meshes on client shutdown
373 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
374 scene::IAnimatedMesh * mesh =
375 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
378 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
382 void Client::connect(Address address)
384 DSTACK(__FUNCTION_NAME);
385 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
386 m_con.SetTimeoutMs(0);
387 m_con.Connect(address);
390 bool Client::connectedAndInitialized()
392 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
394 if(m_con.Connected() == false)
397 if(m_server_ser_ver == SER_FMT_VER_INVALID)
403 void Client::step(float dtime)
405 DSTACK(__FUNCTION_NAME);
411 if(m_ignore_damage_timer > dtime)
412 m_ignore_damage_timer -= dtime;
414 m_ignore_damage_timer = 0.0;
416 m_animation_time += dtime;
417 if(m_animation_time > 60.0)
418 m_animation_time -= 60.0;
420 m_time_of_day_update_timer += dtime;
422 //infostream<<"Client steps "<<dtime<<std::endl;
425 //TimeTaker timer("ReceiveAll()", m_device);
431 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
433 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
434 m_con.RunTimeouts(dtime);
441 float &counter = m_packetcounter_timer;
447 infostream<<"Client packetcounter (20s):"<<std::endl;
448 m_packetcounter.print(infostream);
449 m_packetcounter.clear();
453 // Get connection status
454 bool connected = connectedAndInitialized();
459 Delete unused sectors
461 NOTE: This jams the game for a while because deleting sectors
465 float &counter = m_delete_unused_sectors_timer;
473 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
475 core::list<v3s16> deleted_blocks;
477 float delete_unused_sectors_timeout =
478 g_settings->getFloat("client_delete_unused_sectors_timeout");
480 // Delete sector blocks
481 /*u32 num = m_env.getMap().unloadUnusedData
482 (delete_unused_sectors_timeout,
483 true, &deleted_blocks);*/
485 // Delete whole sectors
486 m_env.getMap().unloadUnusedData
487 (delete_unused_sectors_timeout,
490 if(deleted_blocks.size() > 0)
492 /*infostream<<"Client: Deleted blocks of "<<num
493 <<" unused sectors"<<std::endl;*/
494 /*infostream<<"Client: Deleted "<<num
495 <<" unused sectors"<<std::endl;*/
501 // Env is locked so con can be locked.
502 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
504 core::list<v3s16>::Iterator i = deleted_blocks.begin();
505 core::list<v3s16> sendlist;
508 if(sendlist.size() == 255 || i == deleted_blocks.end())
510 if(sendlist.size() == 0)
519 u32 replysize = 2+1+6*sendlist.size();
520 SharedBuffer<u8> reply(replysize);
521 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
522 reply[2] = sendlist.size();
524 for(core::list<v3s16>::Iterator
525 j = sendlist.begin();
526 j != sendlist.end(); j++)
528 writeV3S16(&reply[2+1+6*k], *j);
531 m_con.Send(PEER_ID_SERVER, 1, reply, true);
533 if(i == deleted_blocks.end())
539 sendlist.push_back(*i);
547 if(connected == false)
549 float &counter = m_connection_reinit_timer;
555 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
557 Player *myplayer = m_env.getLocalPlayer();
558 assert(myplayer != NULL);
560 // Send TOSERVER_INIT
561 // [0] u16 TOSERVER_INIT
562 // [2] u8 SER_FMT_VER_HIGHEST_READ
563 // [3] u8[20] player_name
564 // [23] u8[28] password (new in some version)
565 // [51] u16 minimum supported network protocol version (added sometime)
566 // [53] u16 maximum supported network protocol version (added later than the previous one)
567 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
568 writeU16(&data[0], TOSERVER_INIT);
569 writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
571 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
572 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
574 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
577 memset((char*)&data[23], 0, PASSWORD_SIZE);
578 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
580 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
581 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
583 // Send as unreliable
584 Send(0, data, false);
587 // Not connected, return
592 Do stuff if connected
596 Run Map's timers and unload unused data
598 const float map_timer_and_unload_dtime = 5.25;
599 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
601 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
602 std::list<v3s16> deleted_blocks;
603 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
604 g_settings->getFloat("client_unload_unused_data_timeout"),
607 /*if(deleted_blocks.size() > 0)
608 infostream<<"Client: Unloaded "<<deleted_blocks.size()
609 <<" unused blocks"<<std::endl;*/
613 NOTE: This loop is intentionally iterated the way it is.
616 std::list<v3s16>::iterator i = deleted_blocks.begin();
617 std::list<v3s16> sendlist;
620 if(sendlist.size() == 255 || i == deleted_blocks.end())
622 if(sendlist.size() == 0)
631 u32 replysize = 2+1+6*sendlist.size();
632 SharedBuffer<u8> reply(replysize);
633 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
634 reply[2] = sendlist.size();
636 for(std::list<v3s16>::iterator
637 j = sendlist.begin();
638 j != sendlist.end(); ++j)
640 writeV3S16(&reply[2+1+6*k], *j);
643 m_con.Send(PEER_ID_SERVER, 1, reply, true);
645 if(i == deleted_blocks.end())
651 sendlist.push_back(*i);
661 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
663 // Control local player (0ms)
664 LocalPlayer *player = m_env.getLocalPlayer();
665 assert(player != NULL);
666 player->applyControl(dtime);
668 //TimeTaker envtimer("env step", m_device);
677 ClientEnvEvent event = m_env.getClientEvent();
678 if(event.type == CEE_NONE)
682 else if(event.type == CEE_PLAYER_DAMAGE)
684 if(m_ignore_damage_timer <= 0)
686 u8 damage = event.player_damage.amount;
688 if(event.player_damage.send_to_server)
691 // Add to ClientEvent queue
693 event.type = CE_PLAYER_DAMAGE;
694 event.player_damage.amount = damage;
695 m_client_event_queue.push_back(event);
698 else if(event.type == CEE_PLAYER_BREATH)
700 u16 breath = event.player_breath.amount;
710 float &counter = m_avg_rtt_timer;
715 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
716 // connectedAndInitialized() is true, peer exists.
717 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
718 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
723 Send player position to server
726 float &counter = m_playerpos_send_timer;
728 if(counter >= m_recommended_send_interval)
736 Replace updated meshes
739 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
741 //TimeTaker timer("** Processing mesh update result queue");
744 /*infostream<<"Mesh update result queue size is "
745 <<m_mesh_update_thread.m_queue_out.size()
748 int num_processed_meshes = 0;
749 while(!m_mesh_update_thread.m_queue_out.empty())
751 num_processed_meshes++;
752 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
753 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
756 //JMutexAutoLock lock(block->mesh_mutex);
758 // Delete the old mesh
759 if(block->mesh != NULL)
761 // TODO: Remove hardware buffers of meshbuffers of block->mesh
766 // Replace with the new mesh
767 block->mesh = r.mesh;
771 if(r.ack_block_to_server)
773 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
774 <<","<<r.p.Z<<")"<<std::endl;*/
785 u32 replysize = 2+1+6;
786 SharedBuffer<u8> reply(replysize);
787 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
789 writeV3S16(&reply[3], r.p);
791 m_con.Send(PEER_ID_SERVER, 1, reply, true);
794 if(num_processed_meshes > 0)
795 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
801 if (m_media_receive_started) {
802 bool all_stopped = true;
803 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
804 thread != m_media_fetch_threads.end(); ++thread) {
805 all_stopped &= !(*thread)->IsRunning();
806 while (!(*thread)->m_file_data.empty()) {
807 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
808 if(m_media_received_count < m_media_count)
809 m_media_received_count++;
811 bool success = loadMedia(out.second, out.first);
813 verbosestream<<"Client: Loaded received media: "
814 <<"\""<<out.first<<"\". Caching."<<std::endl;
816 infostream<<"Client: Failed to load received media: "
817 <<"\""<<out.first<<"\". Not caching."<<std::endl;
821 bool did = fs::CreateAllDirs(getMediaCacheDir());
823 errorstream<<"Could not create media cache directory"
828 std::map<std::string, std::string>::iterator n;
829 n = m_media_name_sha1_map.find(out.first);
830 if(n == m_media_name_sha1_map.end())
831 errorstream<<"The server sent a file that has not "
832 <<"been announced."<<std::endl;
834 m_media_cache.update_sha1(out.second);
839 std::list<MediaRequest> fetch_failed;
840 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
841 thread != m_media_fetch_threads.end(); ++thread) {
842 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
843 request != (*thread)->m_failed.end(); ++request)
844 fetch_failed.push_back(*request);
845 (*thread)->m_failed.clear();
847 if (fetch_failed.size() > 0) {
848 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
849 << "Requesting them the usual way." << std::endl;
850 request_media(fetch_failed);
856 If the server didn't update the inventory in a while, revert
857 the local inventory (so the player notices the lag problem
858 and knows something is wrong).
860 if(m_inventory_from_server)
862 float interval = 10.0;
863 float count_before = floor(m_inventory_from_server_age / interval);
865 m_inventory_from_server_age += dtime;
867 float count_after = floor(m_inventory_from_server_age / interval);
869 if(count_after != count_before)
871 // Do this every <interval> seconds after TOCLIENT_INVENTORY
872 // Reset the locally changed inventory to the authoritative inventory
873 Player *player = m_env.getLocalPlayer();
874 player->inventory = *m_inventory_from_server;
875 m_inventory_updated = true;
880 Update positions of sounds attached to objects
883 for(std::map<int, u16>::iterator
884 i = m_sounds_to_objects.begin();
885 i != m_sounds_to_objects.end(); i++)
887 int client_id = i->first;
888 u16 object_id = i->second;
889 ClientActiveObject *cao = m_env.getActiveObject(object_id);
892 v3f pos = cao->getPosition();
893 m_sound->updateSoundPosition(client_id, pos);
898 Handle removed remotely initiated sounds
900 m_removed_sounds_check_timer += dtime;
901 if(m_removed_sounds_check_timer >= 2.32)
903 m_removed_sounds_check_timer = 0;
904 // Find removed sounds and clear references to them
905 std::set<s32> removed_server_ids;
906 for(std::map<s32, int>::iterator
907 i = m_sounds_server_to_client.begin();
908 i != m_sounds_server_to_client.end();)
910 s32 server_id = i->first;
911 int client_id = i->second;
913 if(!m_sound->soundExists(client_id)){
914 m_sounds_server_to_client.erase(server_id);
915 m_sounds_client_to_server.erase(client_id);
916 m_sounds_to_objects.erase(client_id);
917 removed_server_ids.insert(server_id);
921 if(removed_server_ids.size() != 0)
923 std::ostringstream os(std::ios_base::binary);
924 writeU16(os, TOSERVER_REMOVED_SOUNDS);
925 writeU16(os, removed_server_ids.size());
926 for(std::set<s32>::iterator i = removed_server_ids.begin();
927 i != removed_server_ids.end(); i++)
929 std::string s = os.str();
930 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
937 bool Client::loadMedia(const std::string &data, const std::string &filename)
939 // Silly irrlicht's const-incorrectness
940 Buffer<char> data_rw(data.c_str(), data.size());
944 const char *image_ext[] = {
945 ".png", ".jpg", ".bmp", ".tga",
946 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
949 name = removeStringEnd(filename, image_ext);
952 verbosestream<<"Client: Attempting to load image "
953 <<"file \""<<filename<<"\""<<std::endl;
955 io::IFileSystem *irrfs = m_device->getFileSystem();
956 video::IVideoDriver *vdrv = m_device->getVideoDriver();
958 // Create an irrlicht memory file
959 io::IReadFile *rfile = irrfs->createMemoryReadFile(
960 *data_rw, data_rw.getSize(), "_tempreadfile");
963 video::IImage *img = vdrv->createImageFromFile(rfile);
965 errorstream<<"Client: Cannot create image from data of "
966 <<"file \""<<filename<<"\""<<std::endl;
971 m_tsrc->insertSourceImage(filename, img);
978 const char *sound_ext[] = {
979 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
980 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
983 name = removeStringEnd(filename, sound_ext);
986 verbosestream<<"Client: Attempting to load sound "
987 <<"file \""<<filename<<"\""<<std::endl;
988 m_sound->loadSoundData(name, data);
992 const char *model_ext[] = {
993 ".x", ".b3d", ".md2", ".obj",
996 name = removeStringEnd(filename, model_ext);
999 verbosestream<<"Client: Storing model into Irrlicht: "
1000 <<"\""<<filename<<"\""<<std::endl;
1001 scene::ISceneManager *smgr = m_device->getSceneManager();
1003 //check if mesh was already cached
1004 scene::IAnimatedMesh *mesh =
1005 smgr->getMeshCache()->getMeshByName(filename.c_str());
1008 errorstream << "Multiple models with name: " << filename.c_str() <<
1009 " found replacing previous model!" << std::endl;
1011 smgr->getMeshCache()->removeMesh(mesh);
1015 io::IFileSystem *irrfs = m_device->getFileSystem();
1016 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1017 *data_rw, data_rw.getSize(), filename.c_str());
1020 mesh = smgr->getMesh(rfile);
1021 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1026 errorstream<<"Client: Don't know how to load file \""
1027 <<filename<<"\""<<std::endl;
1031 // Virtual methods from con::PeerHandler
1032 void Client::peerAdded(con::Peer *peer)
1034 infostream<<"Client::peerAdded(): peer->id="
1035 <<peer->id<<std::endl;
1037 void Client::deletingPeer(con::Peer *peer, bool timeout)
1039 infostream<<"Client::deletingPeer(): "
1040 "Server Peer is getting deleted "
1041 <<"(timeout="<<timeout<<")"<<std::endl;
1046 u16 number of files requested
1052 void Client::request_media(const std::list<MediaRequest> &file_requests)
1054 std::ostringstream os(std::ios_base::binary);
1055 writeU16(os, TOSERVER_REQUEST_MEDIA);
1056 writeU16(os, file_requests.size());
1058 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1059 i != file_requests.end(); ++i) {
1060 os<<serializeString(i->name);
1064 std::string s = os.str();
1065 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1067 Send(0, data, true);
1068 infostream<<"Client: Sending media request list to server ("
1069 <<file_requests.size()<<" files)"<<std::endl;
1072 void Client::ReceiveAll()
1074 DSTACK(__FUNCTION_NAME);
1075 u32 start_ms = porting::getTimeMs();
1078 // Limit time even if there would be huge amounts of data to
1080 if(porting::getTimeMs() > start_ms + 100)
1085 g_profiler->graphAdd("client_received_packets", 1);
1087 catch(con::NoIncomingDataException &e)
1091 catch(con::InvalidIncomingDataException &e)
1093 infostream<<"Client::ReceiveAll(): "
1094 "InvalidIncomingDataException: what()="
1095 <<e.what()<<std::endl;
1100 void Client::Receive()
1102 DSTACK(__FUNCTION_NAME);
1103 SharedBuffer<u8> data;
1107 //TimeTaker t1("con mutex and receive", m_device);
1108 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1109 datasize = m_con.Receive(sender_peer_id, data);
1111 //TimeTaker t1("ProcessData", m_device);
1112 ProcessData(*data, datasize, sender_peer_id);
1116 sender_peer_id given to this shall be quaranteed to be a valid peer
1118 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1120 DSTACK(__FUNCTION_NAME);
1122 // Ignore packets that don't even fit a command
1125 m_packetcounter.add(60000);
1129 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1131 //infostream<<"Client: received command="<<command<<std::endl;
1132 m_packetcounter.add((u16)command);
1135 If this check is removed, be sure to change the queue
1136 system to know the ids
1138 if(sender_peer_id != PEER_ID_SERVER)
1140 infostream<<"Client::ProcessData(): Discarding data not "
1141 "coming from server: peer_id="<<sender_peer_id
1146 u8 ser_version = m_server_ser_ver;
1148 //infostream<<"Client received command="<<(int)command<<std::endl;
1150 if(command == TOCLIENT_INIT)
1155 u8 deployed = data[2];
1157 infostream<<"Client: TOCLIENT_INIT received with "
1158 "deployed="<<((int)deployed&0xff)<<std::endl;
1160 if(!ser_ver_supported(deployed))
1162 infostream<<"Client: TOCLIENT_INIT: Server sent "
1163 <<"unsupported ser_fmt_ver"<<std::endl;
1167 m_server_ser_ver = deployed;
1169 // Get player position
1170 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1171 if(datasize >= 2+1+6)
1172 playerpos_s16 = readV3S16(&data[2+1]);
1173 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1176 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1178 // Set player position
1179 Player *player = m_env.getLocalPlayer();
1180 assert(player != NULL);
1181 player->setPosition(playerpos_f);
1184 if(datasize >= 2+1+6+8)
1187 m_map_seed = readU64(&data[2+1+6]);
1188 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1191 if(datasize >= 2+1+6+8+4)
1194 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1195 infostream<<"Client: received recommended send interval "
1196 <<m_recommended_send_interval<<std::endl;
1201 SharedBuffer<u8> reply(replysize);
1202 writeU16(&reply[0], TOSERVER_INIT2);
1204 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1209 if(command == TOCLIENT_ACCESS_DENIED)
1211 // The server didn't like our password. Note, this needs
1212 // to be processed even if the serialisation format has
1213 // not been agreed yet, the same as TOCLIENT_INIT.
1214 m_access_denied = true;
1215 m_access_denied_reason = L"Unknown";
1218 std::string datastring((char*)&data[2], datasize-2);
1219 std::istringstream is(datastring, std::ios_base::binary);
1220 m_access_denied_reason = deSerializeWideString(is);
1225 if(ser_version == SER_FMT_VER_INVALID)
1227 infostream<<"Client: Server serialization"
1228 " format invalid or not initialized."
1229 " Skipping incoming command="<<command<<std::endl;
1233 // Just here to avoid putting the two if's together when
1234 // making some copypasta
1237 if(command == TOCLIENT_REMOVENODE)
1242 p.X = readS16(&data[2]);
1243 p.Y = readS16(&data[4]);
1244 p.Z = readS16(&data[6]);
1246 //TimeTaker t1("TOCLIENT_REMOVENODE");
1250 else if(command == TOCLIENT_ADDNODE)
1252 if(datasize < 8 + MapNode::serializedLength(ser_version))
1256 p.X = readS16(&data[2]);
1257 p.Y = readS16(&data[4]);
1258 p.Z = readS16(&data[6]);
1260 //TimeTaker t1("TOCLIENT_ADDNODE");
1263 n.deSerialize(&data[8], ser_version);
1267 else if(command == TOCLIENT_BLOCKDATA)
1269 // Ignore too small packet
1274 p.X = readS16(&data[2]);
1275 p.Y = readS16(&data[4]);
1276 p.Z = readS16(&data[6]);
1278 /*infostream<<"Client: Thread: BLOCKDATA for ("
1279 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1280 /*infostream<<"Client: Thread: BLOCKDATA for ("
1281 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1283 std::string datastring((char*)&data[8], datasize-8);
1284 std::istringstream istr(datastring, std::ios_base::binary);
1289 v2s16 p2d(p.X, p.Z);
1290 sector = m_env.getMap().emergeSector(p2d);
1292 assert(sector->getPos() == p2d);
1294 //TimeTaker timer("MapBlock deSerialize");
1297 block = sector->getBlockNoCreateNoEx(p.Y);
1301 Update an existing block
1303 //infostream<<"Updating"<<std::endl;
1304 block->deSerialize(istr, ser_version, false);
1305 block->deSerializeNetworkSpecific(istr);
1312 //infostream<<"Creating new"<<std::endl;
1313 block = new MapBlock(&m_env.getMap(), p, this);
1314 block->deSerialize(istr, ser_version, false);
1315 block->deSerializeNetworkSpecific(istr);
1316 sector->insertBlock(block);
1330 u32 replysize = 2+1+6;
1331 SharedBuffer<u8> reply(replysize);
1332 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1334 writeV3S16(&reply[3], p);
1336 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1340 Add it to mesh update queue and set it to be acknowledged after update.
1342 //infostream<<"Adding mesh update task for received block"<<std::endl;
1343 addUpdateMeshTaskWithEdge(p, true);
1345 else if(command == TOCLIENT_INVENTORY)
1350 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1353 //TimeTaker t2("mutex locking", m_device);
1354 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1357 //TimeTaker t3("istringstream init", m_device);
1358 std::string datastring((char*)&data[2], datasize-2);
1359 std::istringstream is(datastring, std::ios_base::binary);
1362 //TimeTaker t4("player get", m_device);
1363 Player *player = m_env.getLocalPlayer();
1364 assert(player != NULL);
1367 //TimeTaker t1("inventory.deSerialize()", m_device);
1368 player->inventory.deSerialize(is);
1371 m_inventory_updated = true;
1373 delete m_inventory_from_server;
1374 m_inventory_from_server = new Inventory(player->inventory);
1375 m_inventory_from_server_age = 0.0;
1377 //infostream<<"Client got player inventory:"<<std::endl;
1378 //player->inventory.print(infostream);
1381 else if(command == TOCLIENT_TIME_OF_DAY)
1386 u16 time_of_day = readU16(&data[2]);
1387 time_of_day = time_of_day % 24000;
1388 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1389 float time_speed = 0;
1390 if(datasize >= 2 + 2 + 4){
1391 time_speed = readF1000(&data[4]);
1393 // Old message; try to approximate speed of time by ourselves
1394 float time_of_day_f = (float)time_of_day / 24000.0;
1395 float tod_diff_f = 0;
1396 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1397 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1399 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1400 m_last_time_of_day_f = time_of_day_f;
1401 float time_diff = m_time_of_day_update_timer;
1402 m_time_of_day_update_timer = 0;
1403 if(m_time_of_day_set){
1404 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1405 infostream<<"Client: Measured time_of_day speed (old format): "
1406 <<time_speed<<" tod_diff_f="<<tod_diff_f
1407 <<" time_diff="<<time_diff<<std::endl;
1411 // Update environment
1412 m_env.setTimeOfDay(time_of_day);
1413 m_env.setTimeOfDaySpeed(time_speed);
1414 m_time_of_day_set = true;
1416 u32 dr = m_env.getDayNightRatio();
1417 verbosestream<<"Client: time_of_day="<<time_of_day
1418 <<" time_speed="<<time_speed
1419 <<" dr="<<dr<<std::endl;
1421 else if(command == TOCLIENT_CHAT_MESSAGE)
1429 std::string datastring((char*)&data[2], datasize-2);
1430 std::istringstream is(datastring, std::ios_base::binary);
1433 is.read((char*)buf, 2);
1434 u16 len = readU16(buf);
1436 std::wstring message;
1437 for(u16 i=0; i<len; i++)
1439 is.read((char*)buf, 2);
1440 message += (wchar_t)readU16(buf);
1443 /*infostream<<"Client received chat message: "
1444 <<wide_to_narrow(message)<<std::endl;*/
1446 m_chat_queue.push_back(message);
1448 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1450 //if(g_settings->getBool("enable_experimental"))
1454 u16 count of removed objects
1455 for all removed objects {
1458 u16 count of added objects
1459 for all added objects {
1462 u32 initialization data length
1463 string initialization data
1468 // Get all data except the command number
1469 std::string datastring((char*)&data[2], datasize-2);
1470 // Throw them in an istringstream
1471 std::istringstream is(datastring, std::ios_base::binary);
1475 // Read removed objects
1477 u16 removed_count = readU16((u8*)buf);
1478 for(u16 i=0; i<removed_count; i++)
1481 u16 id = readU16((u8*)buf);
1484 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1485 m_env.removeActiveObject(id);
1489 // Read added objects
1491 u16 added_count = readU16((u8*)buf);
1492 for(u16 i=0; i<added_count; i++)
1495 u16 id = readU16((u8*)buf);
1497 u8 type = readU8((u8*)buf);
1498 std::string data = deSerializeLongString(is);
1501 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1502 m_env.addActiveObject(id, type, data);
1507 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1509 //if(g_settings->getBool("enable_experimental"))
1521 // Get all data except the command number
1522 std::string datastring((char*)&data[2], datasize-2);
1523 // Throw them in an istringstream
1524 std::istringstream is(datastring, std::ios_base::binary);
1526 while(is.eof() == false)
1530 u16 id = readU16((u8*)buf);
1534 u16 message_size = readU16((u8*)buf);
1535 std::string message;
1536 message.reserve(message_size);
1537 for(u16 i=0; i<message_size; i++)
1540 message.append(buf, 1);
1542 // Pass on to the environment
1544 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1545 m_env.processActiveObjectMessage(id, message);
1550 else if(command == TOCLIENT_MOVEMENT)
1552 std::string datastring((char*)&data[2], datasize-2);
1553 std::istringstream is(datastring, std::ios_base::binary);
1554 Player *player = m_env.getLocalPlayer();
1555 assert(player != NULL);
1557 player->movement_acceleration_default = readF1000(is) * BS;
1558 player->movement_acceleration_air = readF1000(is) * BS;
1559 player->movement_acceleration_fast = readF1000(is) * BS;
1560 player->movement_speed_walk = readF1000(is) * BS;
1561 player->movement_speed_crouch = readF1000(is) * BS;
1562 player->movement_speed_fast = readF1000(is) * BS;
1563 player->movement_speed_climb = readF1000(is) * BS;
1564 player->movement_speed_jump = readF1000(is) * BS;
1565 player->movement_liquid_fluidity = readF1000(is) * BS;
1566 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1567 player->movement_liquid_sink = readF1000(is) * BS;
1568 player->movement_gravity = readF1000(is) * BS;
1570 else if(command == TOCLIENT_HP)
1572 std::string datastring((char*)&data[2], datasize-2);
1573 std::istringstream is(datastring, std::ios_base::binary);
1574 Player *player = m_env.getLocalPlayer();
1575 assert(player != NULL);
1576 u8 oldhp = player->hp;
1582 // Add to ClientEvent queue
1584 event.type = CE_PLAYER_DAMAGE;
1585 event.player_damage.amount = oldhp - hp;
1586 m_client_event_queue.push_back(event);
1589 else if(command == TOCLIENT_BREATH)
1591 std::string datastring((char*)&data[2], datasize-2);
1592 std::istringstream is(datastring, std::ios_base::binary);
1593 Player *player = m_env.getLocalPlayer();
1594 assert(player != NULL);
1595 u16 breath = readU16(is);
1596 player->setBreath(breath) ;
1598 else if(command == TOCLIENT_MOVE_PLAYER)
1600 std::string datastring((char*)&data[2], datasize-2);
1601 std::istringstream is(datastring, std::ios_base::binary);
1602 Player *player = m_env.getLocalPlayer();
1603 assert(player != NULL);
1604 v3f pos = readV3F1000(is);
1605 f32 pitch = readF1000(is);
1606 f32 yaw = readF1000(is);
1607 player->setPosition(pos);
1608 /*player->setPitch(pitch);
1609 player->setYaw(yaw);*/
1611 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1612 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1618 Add to ClientEvent queue.
1619 This has to be sent to the main program because otherwise
1620 it would just force the pitch and yaw values to whatever
1621 the camera points to.
1624 event.type = CE_PLAYER_FORCE_MOVE;
1625 event.player_force_move.pitch = pitch;
1626 event.player_force_move.yaw = yaw;
1627 m_client_event_queue.push_back(event);
1629 // Ignore damage for a few seconds, so that the player doesn't
1630 // get damage from falling on ground
1631 m_ignore_damage_timer = 3.0;
1633 else if(command == TOCLIENT_PLAYERITEM)
1635 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1637 else if(command == TOCLIENT_DEATHSCREEN)
1639 std::string datastring((char*)&data[2], datasize-2);
1640 std::istringstream is(datastring, std::ios_base::binary);
1642 bool set_camera_point_target = readU8(is);
1643 v3f camera_point_target = readV3F1000(is);
1646 event.type = CE_DEATHSCREEN;
1647 event.deathscreen.set_camera_point_target = set_camera_point_target;
1648 event.deathscreen.camera_point_target_x = camera_point_target.X;
1649 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1650 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1651 m_client_event_queue.push_back(event);
1653 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1655 std::string datastring((char*)&data[2], datasize-2);
1656 std::istringstream is(datastring, std::ios_base::binary);
1658 // Mesh update thread must be stopped while
1659 // updating content definitions
1660 assert(!m_mesh_update_thread.IsRunning());
1662 int num_files = readU16(is);
1664 infostream<<"Client: Received media announcement: packet size: "
1665 <<datasize<<std::endl;
1667 std::list<MediaRequest> file_requests;
1669 for(int i=0; i<num_files; i++)
1671 //read file from cache
1672 std::string name = deSerializeString(is);
1673 std::string sha1_base64 = deSerializeString(is);
1675 // if name contains illegal characters, ignore the file
1676 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1677 errorstream<<"Client: ignoring illegal file name "
1678 <<"sent by server: \""<<name<<"\""<<std::endl;
1682 std::string sha1_raw = base64_decode(sha1_base64);
1683 std::string sha1_hex = hex_encode(sha1_raw);
1684 std::ostringstream tmp_os(std::ios_base::binary);
1685 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1686 m_media_name_sha1_map[name] = sha1_raw;
1688 // If found in cache, try to load it from there
1691 bool success = loadMedia(tmp_os.str(), name);
1693 verbosestream<<"Client: Loaded cached media: "
1694 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1697 infostream<<"Client: Failed to load cached media: "
1698 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1701 // Didn't load from cache; queue it to be requested
1702 verbosestream<<"Client: Adding file to request list: \""
1703 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1704 file_requests.push_back(MediaRequest(name));
1707 std::string remote_media = "";
1709 remote_media = deSerializeString(is);
1711 catch(SerializationError) {
1712 // not supported by server or turned off
1715 m_media_count = file_requests.size();
1716 m_media_receive_started = true;
1718 if (remote_media == "" || !USE_CURL) {
1719 request_media(file_requests);
1722 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1723 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1724 i != file_requests.end(); ++i) {
1725 (*cur)->m_file_requests.push_back(*i);
1727 if (cur == m_media_fetch_threads.end())
1728 cur = m_media_fetch_threads.begin();
1730 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1731 i != m_media_fetch_threads.end(); ++i) {
1732 (*i)->m_remote_url = remote_media;
1737 // notify server we received everything
1738 std::ostringstream os(std::ios_base::binary);
1739 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1740 std::string s = os.str();
1741 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1743 Send(0, data, true);
1746 event.type = CE_TEXTURES_UPDATED;
1747 m_client_event_queue.push_back(event);
1749 else if(command == TOCLIENT_MEDIA)
1751 std::string datastring((char*)&data[2], datasize-2);
1752 std::istringstream is(datastring, std::ios_base::binary);
1756 u16 total number of file bunches
1757 u16 index of this bunch
1758 u32 number of files in this bunch
1766 int num_bunches = readU16(is);
1767 int bunch_i = readU16(is);
1768 u32 num_files = readU32(is);
1769 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1770 <<num_bunches<<" files="<<num_files
1771 <<" size="<<datasize<<std::endl;
1773 // Check total and received media count
1774 assert(m_media_received_count <= m_media_count);
1775 if (num_files > m_media_count - m_media_received_count) {
1776 errorstream<<"Client: Received more files than requested:"
1777 <<" total count="<<m_media_count
1778 <<" total received="<<m_media_received_count
1779 <<" bunch "<<bunch_i<<"/"<<num_bunches
1780 <<" files="<<num_files
1781 <<" size="<<datasize<<std::endl;
1782 num_files = m_media_count - m_media_received_count;
1787 // Mesh update thread must be stopped while
1788 // updating content definitions
1789 assert(!m_mesh_update_thread.IsRunning());
1791 for(u32 i=0; i<num_files; i++){
1792 assert(m_media_received_count < m_media_count);
1793 m_media_received_count++;
1794 std::string name = deSerializeString(is);
1795 std::string data = deSerializeLongString(is);
1797 // if name contains illegal characters, ignore the file
1798 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1799 errorstream<<"Client: ignoring illegal file name "
1800 <<"sent by server: \""<<name<<"\""<<std::endl;
1804 bool success = loadMedia(data, name);
1806 verbosestream<<"Client: Loaded received media: "
1807 <<"\""<<name<<"\". Caching."<<std::endl;
1809 infostream<<"Client: Failed to load received media: "
1810 <<"\""<<name<<"\". Not caching."<<std::endl;
1814 bool did = fs::CreateAllDirs(getMediaCacheDir());
1816 errorstream<<"Could not create media cache directory"
1821 std::map<std::string, std::string>::iterator n;
1822 n = m_media_name_sha1_map.find(name);
1823 if(n == m_media_name_sha1_map.end())
1824 errorstream<<"The server sent a file that has not "
1825 <<"been announced."<<std::endl;
1827 m_media_cache.update_sha1(data);
1832 event.type = CE_TEXTURES_UPDATED;
1833 m_client_event_queue.push_back(event);
1835 else if(command == TOCLIENT_TOOLDEF)
1837 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1839 else if(command == TOCLIENT_NODEDEF)
1841 infostream<<"Client: Received node definitions: packet size: "
1842 <<datasize<<std::endl;
1844 // Mesh update thread must be stopped while
1845 // updating content definitions
1846 assert(!m_mesh_update_thread.IsRunning());
1848 // Decompress node definitions
1849 std::string datastring((char*)&data[2], datasize-2);
1850 std::istringstream is(datastring, std::ios_base::binary);
1851 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1852 std::ostringstream tmp_os;
1853 decompressZlib(tmp_is, tmp_os);
1855 // Deserialize node definitions
1856 std::istringstream tmp_is2(tmp_os.str());
1857 m_nodedef->deSerialize(tmp_is2);
1858 m_nodedef_received = true;
1860 else if(command == TOCLIENT_CRAFTITEMDEF)
1862 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1864 else if(command == TOCLIENT_ITEMDEF)
1866 infostream<<"Client: Received item definitions: packet size: "
1867 <<datasize<<std::endl;
1869 // Mesh update thread must be stopped while
1870 // updating content definitions
1871 assert(!m_mesh_update_thread.IsRunning());
1873 // Decompress item definitions
1874 std::string datastring((char*)&data[2], datasize-2);
1875 std::istringstream is(datastring, std::ios_base::binary);
1876 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1877 std::ostringstream tmp_os;
1878 decompressZlib(tmp_is, tmp_os);
1880 // Deserialize node definitions
1881 std::istringstream tmp_is2(tmp_os.str());
1882 m_itemdef->deSerialize(tmp_is2);
1883 m_itemdef_received = true;
1885 else if(command == TOCLIENT_PLAY_SOUND)
1887 std::string datastring((char*)&data[2], datasize-2);
1888 std::istringstream is(datastring, std::ios_base::binary);
1890 s32 server_id = readS32(is);
1891 std::string name = deSerializeString(is);
1892 float gain = readF1000(is);
1893 int type = readU8(is); // 0=local, 1=positional, 2=object
1894 v3f pos = readV3F1000(is);
1895 u16 object_id = readU16(is);
1896 bool loop = readU8(is);
1901 client_id = m_sound->playSound(name, loop, gain);
1903 case 1: // positional
1904 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1907 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1909 pos = cao->getPosition();
1910 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1911 // TODO: Set up sound to move with object
1916 if(client_id != -1){
1917 m_sounds_server_to_client[server_id] = client_id;
1918 m_sounds_client_to_server[client_id] = server_id;
1920 m_sounds_to_objects[client_id] = object_id;
1923 else if(command == TOCLIENT_STOP_SOUND)
1925 std::string datastring((char*)&data[2], datasize-2);
1926 std::istringstream is(datastring, std::ios_base::binary);
1928 s32 server_id = readS32(is);
1929 std::map<s32, int>::iterator i =
1930 m_sounds_server_to_client.find(server_id);
1931 if(i != m_sounds_server_to_client.end()){
1932 int client_id = i->second;
1933 m_sound->stopSound(client_id);
1936 else if(command == TOCLIENT_PRIVILEGES)
1938 std::string datastring((char*)&data[2], datasize-2);
1939 std::istringstream is(datastring, std::ios_base::binary);
1941 m_privileges.clear();
1942 infostream<<"Client: Privileges updated: ";
1943 u16 num_privileges = readU16(is);
1944 for(u16 i=0; i<num_privileges; i++){
1945 std::string priv = deSerializeString(is);
1946 m_privileges.insert(priv);
1947 infostream<<priv<<" ";
1949 infostream<<std::endl;
1951 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1953 std::string datastring((char*)&data[2], datasize-2);
1954 std::istringstream is(datastring, std::ios_base::binary);
1956 // Store formspec in LocalPlayer
1957 Player *player = m_env.getLocalPlayer();
1958 assert(player != NULL);
1959 player->inventory_formspec = deSerializeLongString(is);
1961 else if(command == TOCLIENT_DETACHED_INVENTORY)
1963 std::string datastring((char*)&data[2], datasize-2);
1964 std::istringstream is(datastring, std::ios_base::binary);
1966 std::string name = deSerializeString(is);
1968 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1970 Inventory *inv = NULL;
1971 if(m_detached_inventories.count(name) > 0)
1972 inv = m_detached_inventories[name];
1974 inv = new Inventory(m_itemdef);
1975 m_detached_inventories[name] = inv;
1977 inv->deSerialize(is);
1979 else if(command == TOCLIENT_SHOW_FORMSPEC)
1981 std::string datastring((char*)&data[2], datasize-2);
1982 std::istringstream is(datastring, std::ios_base::binary);
1984 std::string formspec = deSerializeLongString(is);
1985 std::string formname = deSerializeString(is);
1988 event.type = CE_SHOW_FORMSPEC;
1989 // pointer is required as event is a struct only!
1990 // adding a std:string to a struct isn't possible
1991 event.show_formspec.formspec = new std::string(formspec);
1992 event.show_formspec.formname = new std::string(formname);
1993 m_client_event_queue.push_back(event);
1995 else if(command == TOCLIENT_SPAWN_PARTICLE)
1997 std::string datastring((char*)&data[2], datasize-2);
1998 std::istringstream is(datastring, std::ios_base::binary);
2000 v3f pos = readV3F1000(is);
2001 v3f vel = readV3F1000(is);
2002 v3f acc = readV3F1000(is);
2003 float expirationtime = readF1000(is);
2004 float size = readF1000(is);
2005 bool collisiondetection = readU8(is);
2006 std::string texture = deSerializeLongString(is);
2009 event.type = CE_SPAWN_PARTICLE;
2010 event.spawn_particle.pos = new v3f (pos);
2011 event.spawn_particle.vel = new v3f (vel);
2012 event.spawn_particle.acc = new v3f (acc);
2014 event.spawn_particle.expirationtime = expirationtime;
2015 event.spawn_particle.size = size;
2016 event.spawn_particle.collisiondetection =
2018 event.spawn_particle.texture = new std::string(texture);
2020 m_client_event_queue.push_back(event);
2022 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2024 std::string datastring((char*)&data[2], datasize-2);
2025 std::istringstream is(datastring, std::ios_base::binary);
2027 u16 amount = readU16(is);
2028 float spawntime = readF1000(is);
2029 v3f minpos = readV3F1000(is);
2030 v3f maxpos = readV3F1000(is);
2031 v3f minvel = readV3F1000(is);
2032 v3f maxvel = readV3F1000(is);
2033 v3f minacc = readV3F1000(is);
2034 v3f maxacc = readV3F1000(is);
2035 float minexptime = readF1000(is);
2036 float maxexptime = readF1000(is);
2037 float minsize = readF1000(is);
2038 float maxsize = readF1000(is);
2039 bool collisiondetection = readU8(is);
2040 std::string texture = deSerializeLongString(is);
2041 u32 id = readU32(is);
2044 event.type = CE_ADD_PARTICLESPAWNER;
2045 event.add_particlespawner.amount = amount;
2046 event.add_particlespawner.spawntime = spawntime;
2048 event.add_particlespawner.minpos = new v3f (minpos);
2049 event.add_particlespawner.maxpos = new v3f (maxpos);
2050 event.add_particlespawner.minvel = new v3f (minvel);
2051 event.add_particlespawner.maxvel = new v3f (maxvel);
2052 event.add_particlespawner.minacc = new v3f (minacc);
2053 event.add_particlespawner.maxacc = new v3f (maxacc);
2055 event.add_particlespawner.minexptime = minexptime;
2056 event.add_particlespawner.maxexptime = maxexptime;
2057 event.add_particlespawner.minsize = minsize;
2058 event.add_particlespawner.maxsize = maxsize;
2059 event.add_particlespawner.collisiondetection = collisiondetection;
2060 event.add_particlespawner.texture = new std::string(texture);
2061 event.add_particlespawner.id = id;
2063 m_client_event_queue.push_back(event);
2065 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2067 std::string datastring((char*)&data[2], datasize-2);
2068 std::istringstream is(datastring, std::ios_base::binary);
2070 u32 id = readU16(is);
2073 event.type = CE_DELETE_PARTICLESPAWNER;
2074 event.delete_particlespawner.id = id;
2076 m_client_event_queue.push_back(event);
2078 else if(command == TOCLIENT_HUDADD)
2080 std::string datastring((char *)&data[2], datasize - 2);
2081 std::istringstream is(datastring, std::ios_base::binary);
2083 u32 id = readU32(is);
2084 u8 type = readU8(is);
2085 v2f pos = readV2F1000(is);
2086 std::string name = deSerializeString(is);
2087 v2f scale = readV2F1000(is);
2088 std::string text = deSerializeString(is);
2089 u32 number = readU32(is);
2090 u32 item = readU32(is);
2091 u32 dir = readU32(is);
2092 v2f align = readV2F1000(is);
2093 v2f offset = readV2F1000(is);
2096 event.type = CE_HUDADD;
2097 event.hudadd.id = id;
2098 event.hudadd.type = type;
2099 event.hudadd.pos = new v2f(pos);
2100 event.hudadd.name = new std::string(name);
2101 event.hudadd.scale = new v2f(scale);
2102 event.hudadd.text = new std::string(text);
2103 event.hudadd.number = number;
2104 event.hudadd.item = item;
2105 event.hudadd.dir = dir;
2106 event.hudadd.align = new v2f(align);
2107 event.hudadd.offset = new v2f(offset);
2108 m_client_event_queue.push_back(event);
2110 else if(command == TOCLIENT_HUDRM)
2112 std::string datastring((char *)&data[2], datasize - 2);
2113 std::istringstream is(datastring, std::ios_base::binary);
2115 u32 id = readU32(is);
2118 event.type = CE_HUDRM;
2119 event.hudrm.id = id;
2120 m_client_event_queue.push_back(event);
2122 else if(command == TOCLIENT_HUDCHANGE)
2128 std::string datastring((char *)&data[2], datasize - 2);
2129 std::istringstream is(datastring, std::ios_base::binary);
2131 u32 id = readU32(is);
2132 u8 stat = (HudElementStat)readU8(is);
2134 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2135 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2136 v2fdata = readV2F1000(is);
2137 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2138 sdata = deSerializeString(is);
2140 intdata = readU32(is);
2143 event.type = CE_HUDCHANGE;
2144 event.hudchange.id = id;
2145 event.hudchange.stat = (HudElementStat)stat;
2146 event.hudchange.v2fdata = new v2f(v2fdata);
2147 event.hudchange.sdata = new std::string(sdata);
2148 event.hudchange.data = intdata;
2149 m_client_event_queue.push_back(event);
2151 else if(command == TOCLIENT_HUD_SET_FLAGS)
2153 std::string datastring((char *)&data[2], datasize - 2);
2154 std::istringstream is(datastring, std::ios_base::binary);
2156 Player *player = m_env.getLocalPlayer();
2157 assert(player != NULL);
2159 u32 flags = readU32(is);
2160 u32 mask = readU32(is);
2162 player->hud_flags &= ~mask;
2163 player->hud_flags |= flags;
2165 else if(command == TOCLIENT_HUD_SET_PARAM)
2167 std::string datastring((char *)&data[2], datasize - 2);
2168 std::istringstream is(datastring, std::ios_base::binary);
2170 Player *player = m_env.getLocalPlayer();
2171 assert(player != NULL);
2173 u16 param = readU16(is);
2174 std::string value = deSerializeString(is);
2176 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2177 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2178 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2179 player->hud_hotbar_itemcount = hotbar_itemcount;
2180 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2181 ((LocalPlayer *) player)->hotbar_image = value;
2182 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2183 ((LocalPlayer *) player)->hotbar_selected_image = value;
2188 infostream<<"Client: Ignoring unknown command "
2189 <<command<<std::endl;
2193 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2195 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2196 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2199 void Client::interact(u8 action, const PointedThing& pointed)
2201 if(connectedAndInitialized() == false){
2202 infostream<<"Client::interact() "
2203 "cancelled (not connected)"
2208 std::ostringstream os(std::ios_base::binary);
2214 [5] u32 length of the next item
2215 [9] serialized PointedThing
2217 0: start digging (from undersurface) or use
2218 1: stop digging (all parameters ignored)
2219 2: digging completed
2220 3: place block or item (to abovesurface)
2223 writeU16(os, TOSERVER_INTERACT);
2224 writeU8(os, action);
2225 writeU16(os, getPlayerItem());
2226 std::ostringstream tmp_os(std::ios::binary);
2227 pointed.serialize(tmp_os);
2228 os<<serializeLongString(tmp_os.str());
2230 std::string s = os.str();
2231 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2234 Send(0, data, true);
2237 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2238 const std::map<std::string, std::string> &fields)
2240 std::ostringstream os(std::ios_base::binary);
2242 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2244 os<<serializeString(formname);
2245 writeU16(os, fields.size());
2246 for(std::map<std::string, std::string>::const_iterator
2247 i = fields.begin(); i != fields.end(); i++){
2248 const std::string &name = i->first;
2249 const std::string &value = i->second;
2250 os<<serializeString(name);
2251 os<<serializeLongString(value);
2255 std::string s = os.str();
2256 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2258 Send(0, data, true);
2261 void Client::sendInventoryFields(const std::string &formname,
2262 const std::map<std::string, std::string> &fields)
2264 std::ostringstream os(std::ios_base::binary);
2266 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2267 os<<serializeString(formname);
2268 writeU16(os, fields.size());
2269 for(std::map<std::string, std::string>::const_iterator
2270 i = fields.begin(); i != fields.end(); i++){
2271 const std::string &name = i->first;
2272 const std::string &value = i->second;
2273 os<<serializeString(name);
2274 os<<serializeLongString(value);
2278 std::string s = os.str();
2279 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2281 Send(0, data, true);
2284 void Client::sendInventoryAction(InventoryAction *a)
2286 std::ostringstream os(std::ios_base::binary);
2290 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2291 os.write((char*)buf, 2);
2296 std::string s = os.str();
2297 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2299 Send(0, data, true);
2302 void Client::sendChatMessage(const std::wstring &message)
2304 std::ostringstream os(std::ios_base::binary);
2308 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2309 os.write((char*)buf, 2);
2312 writeU16(buf, message.size());
2313 os.write((char*)buf, 2);
2316 for(u32 i=0; i<message.size(); i++)
2320 os.write((char*)buf, 2);
2324 std::string s = os.str();
2325 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2327 Send(0, data, true);
2330 void Client::sendChangePassword(const std::wstring oldpassword,
2331 const std::wstring newpassword)
2333 Player *player = m_env.getLocalPlayer();
2337 std::string playername = player->getName();
2338 std::string oldpwd = translatePassword(playername, oldpassword);
2339 std::string newpwd = translatePassword(playername, newpassword);
2341 std::ostringstream os(std::ios_base::binary);
2342 u8 buf[2+PASSWORD_SIZE*2];
2344 [0] u16 TOSERVER_PASSWORD
2345 [2] u8[28] old password
2346 [30] u8[28] new password
2349 writeU16(buf, TOSERVER_PASSWORD);
2350 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2352 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2353 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2355 buf[2+PASSWORD_SIZE-1] = 0;
2356 buf[30+PASSWORD_SIZE-1] = 0;
2357 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2360 std::string s = os.str();
2361 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2363 Send(0, data, true);
2367 void Client::sendDamage(u8 damage)
2369 DSTACK(__FUNCTION_NAME);
2370 std::ostringstream os(std::ios_base::binary);
2372 writeU16(os, TOSERVER_DAMAGE);
2373 writeU8(os, damage);
2376 std::string s = os.str();
2377 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2379 Send(0, data, true);
2382 void Client::sendBreath(u16 breath)
2384 DSTACK(__FUNCTION_NAME);
2385 std::ostringstream os(std::ios_base::binary);
2387 writeU16(os, TOSERVER_BREATH);
2388 writeU16(os, breath);
2390 std::string s = os.str();
2391 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2393 Send(0, data, true);
2396 void Client::sendRespawn()
2398 DSTACK(__FUNCTION_NAME);
2399 std::ostringstream os(std::ios_base::binary);
2401 writeU16(os, TOSERVER_RESPAWN);
2404 std::string s = os.str();
2405 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2407 Send(0, data, true);
2410 void Client::sendPlayerPos()
2412 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2414 LocalPlayer *myplayer = m_env.getLocalPlayer();
2415 if(myplayer == NULL)
2418 // Save bandwidth by only updating position when something changed
2419 if(myplayer->last_position == myplayer->getPosition() &&
2420 myplayer->last_speed == myplayer->getSpeed() &&
2421 myplayer->last_pitch == myplayer->getPitch() &&
2422 myplayer->last_yaw == myplayer->getYaw() &&
2423 myplayer->last_keyPressed == myplayer->keyPressed)
2426 myplayer->last_position = myplayer->getPosition();
2427 myplayer->last_speed = myplayer->getSpeed();
2428 myplayer->last_pitch = myplayer->getPitch();
2429 myplayer->last_yaw = myplayer->getYaw();
2430 myplayer->last_keyPressed = myplayer->keyPressed;
2434 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2435 our_peer_id = m_con.GetPeerID();
2438 // Set peer id if not set already
2439 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2440 myplayer->peer_id = our_peer_id;
2441 // Check that an existing peer_id is the same as the connection's
2442 assert(myplayer->peer_id == our_peer_id);
2444 v3f pf = myplayer->getPosition();
2445 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2446 v3f sf = myplayer->getSpeed();
2447 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2448 s32 pitch = myplayer->getPitch() * 100;
2449 s32 yaw = myplayer->getYaw() * 100;
2450 u32 keyPressed=myplayer->keyPressed;
2454 [2] v3s32 position*100
2455 [2+12] v3s32 speed*100
2456 [2+12+12] s32 pitch*100
2457 [2+12+12+4] s32 yaw*100
2458 [2+12+12+4+4] u32 keyPressed
2460 SharedBuffer<u8> data(2+12+12+4+4+4);
2461 writeU16(&data[0], TOSERVER_PLAYERPOS);
2462 writeV3S32(&data[2], position);
2463 writeV3S32(&data[2+12], speed);
2464 writeS32(&data[2+12+12], pitch);
2465 writeS32(&data[2+12+12+4], yaw);
2466 writeU32(&data[2+12+12+4+4], keyPressed);
2467 // Send as unreliable
2468 Send(0, data, false);
2471 void Client::sendPlayerItem(u16 item)
2473 Player *myplayer = m_env.getLocalPlayer();
2474 if(myplayer == NULL)
2477 u16 our_peer_id = m_con.GetPeerID();
2479 // Set peer id if not set already
2480 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2481 myplayer->peer_id = our_peer_id;
2482 // Check that an existing peer_id is the same as the connection's
2483 assert(myplayer->peer_id == our_peer_id);
2485 SharedBuffer<u8> data(2+2);
2486 writeU16(&data[0], TOSERVER_PLAYERITEM);
2487 writeU16(&data[2], item);
2490 Send(0, data, true);
2493 void Client::removeNode(v3s16 p)
2495 std::map<v3s16, MapBlock*> modified_blocks;
2499 //TimeTaker t("removeNodeAndUpdate", m_device);
2500 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2502 catch(InvalidPositionException &e)
2506 // add urgent task to update the modified node
2507 addUpdateMeshTaskForNode(p, false, true);
2509 for(std::map<v3s16, MapBlock * >::iterator
2510 i = modified_blocks.begin();
2511 i != modified_blocks.end(); ++i)
2513 addUpdateMeshTaskWithEdge(i->first);
2517 void Client::addNode(v3s16 p, MapNode n)
2519 TimeTaker timer1("Client::addNode()");
2521 std::map<v3s16, MapBlock*> modified_blocks;
2525 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2526 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2528 catch(InvalidPositionException &e)
2531 for(std::map<v3s16, MapBlock * >::iterator
2532 i = modified_blocks.begin();
2533 i != modified_blocks.end(); ++i)
2535 addUpdateMeshTaskWithEdge(i->first);
2539 void Client::setPlayerControl(PlayerControl &control)
2541 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2542 LocalPlayer *player = m_env.getLocalPlayer();
2543 assert(player != NULL);
2544 player->control = control;
2547 void Client::selectPlayerItem(u16 item)
2549 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2550 m_playeritem = item;
2551 m_inventory_updated = true;
2552 sendPlayerItem(item);
2555 // Returns true if the inventory of the local player has been
2556 // updated from the server. If it is true, it is set to false.
2557 bool Client::getLocalInventoryUpdated()
2559 // m_inventory_updated is behind envlock
2560 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2561 bool updated = m_inventory_updated;
2562 m_inventory_updated = false;
2566 // Copies the inventory of the local player to parameter
2567 void Client::getLocalInventory(Inventory &dst)
2569 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2570 Player *player = m_env.getLocalPlayer();
2571 assert(player != NULL);
2572 dst = player->inventory;
2575 Inventory* Client::getInventory(const InventoryLocation &loc)
2578 case InventoryLocation::UNDEFINED:
2581 case InventoryLocation::CURRENT_PLAYER:
2583 Player *player = m_env.getLocalPlayer();
2584 assert(player != NULL);
2585 return &player->inventory;
2588 case InventoryLocation::PLAYER:
2590 Player *player = m_env.getPlayer(loc.name.c_str());
2593 return &player->inventory;
2596 case InventoryLocation::NODEMETA:
2598 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2601 return meta->getInventory();
2604 case InventoryLocation::DETACHED:
2606 if(m_detached_inventories.count(loc.name) == 0)
2608 return m_detached_inventories[loc.name];
2616 void Client::inventoryAction(InventoryAction *a)
2619 Send it to the server
2621 sendInventoryAction(a);
2624 Predict some local inventory changes
2626 a->clientApply(this, this);
2632 ClientActiveObject * Client::getSelectedActiveObject(
2634 v3f from_pos_f_on_map,
2635 core::line3d<f32> shootline_on_map
2638 std::vector<DistanceSortedActiveObject> objects;
2640 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2642 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2645 // After this, the closest object is the first in the array.
2646 std::sort(objects.begin(), objects.end());
2648 for(u32 i=0; i<objects.size(); i++)
2650 ClientActiveObject *obj = objects[i].obj;
2652 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2653 if(selection_box == NULL)
2656 v3f pos = obj->getPosition();
2658 core::aabbox3d<f32> offsetted_box(
2659 selection_box->MinEdge + pos,
2660 selection_box->MaxEdge + pos
2663 if(offsetted_box.intersectsWithLine(shootline_on_map))
2665 //infostream<<"Returning selected object"<<std::endl;
2670 //infostream<<"No object selected; returning NULL."<<std::endl;
2674 void Client::printDebugInfo(std::ostream &os)
2676 //JMutexAutoLock lock1(m_fetchblock_mutex);
2677 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2679 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2680 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2681 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2685 std::list<std::string> Client::getConnectedPlayerNames()
2687 return m_env.getPlayerNames();
2690 float Client::getAnimationTime()
2692 return m_animation_time;
2695 int Client::getCrackLevel()
2697 return m_crack_level;
2700 void Client::setCrack(int level, v3s16 pos)
2702 int old_crack_level = m_crack_level;
2703 v3s16 old_crack_pos = m_crack_pos;
2705 m_crack_level = level;
2708 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2711 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2713 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2716 addUpdateMeshTaskForNode(pos, false, true);
2722 Player *player = m_env.getLocalPlayer();
2723 assert(player != NULL);
2727 u16 Client::getBreath()
2729 Player *player = m_env.getLocalPlayer();
2730 assert(player != NULL);
2731 return player->getBreath();
2734 bool Client::getChatMessage(std::wstring &message)
2736 if(m_chat_queue.size() == 0)
2738 message = m_chat_queue.pop_front();
2742 void Client::typeChatMessage(const std::wstring &message)
2744 // Discard empty line
2749 sendChatMessage(message);
2752 if (message[0] == L'/')
2754 m_chat_queue.push_back(
2755 (std::wstring)L"issued command: "+message);
2759 LocalPlayer *player = m_env.getLocalPlayer();
2760 assert(player != NULL);
2761 std::wstring name = narrow_to_wide(player->getName());
2762 m_chat_queue.push_back(
2763 (std::wstring)L"<"+name+L"> "+message);
2767 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2769 /*infostream<<"Client::addUpdateMeshTask(): "
2770 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2771 <<" ack_to_server="<<ack_to_server
2772 <<" urgent="<<urgent
2775 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2780 Create a task to update the mesh of the block
2783 MeshMakeData *data = new MeshMakeData(this);
2786 //TimeTaker timer("data fill");
2788 // Debug: 1-6ms, avg=2ms
2790 data->setCrack(m_crack_level, m_crack_pos);
2791 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2795 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2797 // Add task to queue
2798 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2800 /*infostream<<"Mesh update input queue size is "
2801 <<m_mesh_update_thread.m_queue_in.size()
2805 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2809 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2810 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2815 v3s16 p = blockpos + v3s16(0,0,0);
2816 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2817 addUpdateMeshTask(p, ack_to_server, urgent);
2819 catch(InvalidPositionException &e){}
2821 for (int i=0;i<6;i++)
2824 v3s16 p = blockpos + g_6dirs[i];
2825 addUpdateMeshTask(p, false, urgent);
2827 catch(InvalidPositionException &e){}
2831 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2835 infostream<<"Client::addUpdateMeshTaskForNode(): "
2836 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2840 v3s16 blockpos = getNodeBlockPos(nodepos);
2841 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2844 v3s16 p = blockpos + v3s16(0,0,0);
2845 addUpdateMeshTask(p, ack_to_server, urgent);
2847 catch(InvalidPositionException &e){}
2849 if(nodepos.X == blockpos_relative.X){
2851 v3s16 p = blockpos + v3s16(-1,0,0);
2852 addUpdateMeshTask(p, false, urgent);
2854 catch(InvalidPositionException &e){}
2856 if(nodepos.Y == blockpos_relative.Y){
2858 v3s16 p = blockpos + v3s16(0,-1,0);
2859 addUpdateMeshTask(p, false, urgent);
2861 catch(InvalidPositionException &e){}
2863 if(nodepos.Z == blockpos_relative.Z){
2865 v3s16 p = blockpos + v3s16(0,0,-1);
2866 addUpdateMeshTask(p, false, urgent);
2868 catch(InvalidPositionException &e){}
2872 ClientEvent Client::getClientEvent()
2874 if(m_client_event_queue.size() == 0)
2877 event.type = CE_NONE;
2880 return m_client_event_queue.pop_front();
2883 void draw_load_screen(const std::wstring &text,
2884 IrrlichtDevice* device, gui::IGUIFont* font,
2885 float dtime=0 ,int percent=0, bool clouds=true);
2886 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2888 infostream<<"Client::afterContentReceived() started"<<std::endl;
2889 assert(m_itemdef_received);
2890 assert(m_nodedef_received);
2891 assert(texturesReceived());
2893 // remove the information about which checksum each texture
2895 m_media_name_sha1_map.clear();
2897 // Rebuild inherited images and recreate textures
2898 infostream<<"- Rebuilding images and textures"<<std::endl;
2899 m_tsrc->rebuildImagesAndTextures();
2902 infostream<<"- Rebuilding shaders"<<std::endl;
2903 m_shsrc->rebuildShaders();
2905 // Update node aliases
2906 infostream<<"- Updating node aliases"<<std::endl;
2907 m_nodedef->updateAliases(m_itemdef);
2909 // Update node textures
2910 infostream<<"- Updating node textures"<<std::endl;
2911 m_nodedef->updateTextures(m_tsrc);
2913 // Preload item textures and meshes if configured to
2914 if(g_settings->getBool("preload_item_visuals"))
2916 verbosestream<<"Updating item textures and meshes"<<std::endl;
2917 wchar_t* text = wgettext("Item textures...");
2918 draw_load_screen(text,device,font,0,0);
2919 std::set<std::string> names = m_itemdef->getAll();
2920 size_t size = names.size();
2923 for(std::set<std::string>::const_iterator
2924 i = names.begin(); i != names.end(); ++i){
2925 // Asking for these caches the result
2926 m_itemdef->getInventoryTexture(*i, this);
2927 m_itemdef->getWieldMesh(*i, this);
2929 percent = count*100/size;
2930 if (count%50 == 0) // only update every 50 item
2931 draw_load_screen(text,device,font,0,percent);
2936 // Start mesh update thread after setting up content definitions
2937 infostream<<"- Starting mesh update thread"<<std::endl;
2938 m_mesh_update_thread.Start();
2940 infostream<<"Client::afterContentReceived() done"<<std::endl;
2943 float Client::getRTT(void)
2946 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2947 } catch(con::PeerNotFoundException &e){
2952 // IGameDef interface
2954 IItemDefManager* Client::getItemDefManager()
2958 INodeDefManager* Client::getNodeDefManager()
2962 ICraftDefManager* Client::getCraftDefManager()
2965 //return m_craftdef;
2967 ITextureSource* Client::getTextureSource()
2971 IShaderSource* Client::getShaderSource()
2975 u16 Client::allocateUnknownNodeId(const std::string &name)
2977 errorstream<<"Client::allocateUnknownNodeId(): "
2978 <<"Client cannot allocate node IDs"<<std::endl;
2980 return CONTENT_IGNORE;
2982 ISoundManager* Client::getSoundManager()
2986 MtEventManager* Client::getEventManager()