3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "clientserver.h"
23 #include "jmutexautolock.h"
27 #include "mapsector.h"
28 #include "mapblock_mesh.h"
34 #include "nodemetadata.h"
38 #include <IFileSystem.h>
41 #include "clientmap.h"
42 #include "filecache.h"
44 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "util/serialize.h"
49 #include "util/directiontables.h"
52 #include <curl/curl.h>
55 static std::string getMediaCacheDir()
57 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 void * MediaFetchThread::Thread()
228 log_register_thread("MediaFetchThread");
230 DSTACK(__FUNCTION_NAME);
232 BEGIN_DEBUG_EXCEPTION_HANDLER
237 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
238 i != m_file_requests.end(); ++i) {
239 curl = curl_easy_init();
241 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
242 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
243 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
244 std::ostringstream stream;
245 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
246 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
247 res = curl_easy_perform(curl);
248 if (res == CURLE_OK) {
249 std::string data = stream.str();
250 m_file_data.push_back(make_pair(i->name, data));
252 m_failed.push_back(*i);
253 infostream << "cURL request failed for " << i->name << std::endl;
255 curl_easy_cleanup(curl);
259 END_DEBUG_EXCEPTION_HANDLER(errorstream)
265 IrrlichtDevice *device,
266 const char *playername,
267 std::string password,
268 MapDrawControl &control,
269 IWritableTextureSource *tsrc,
270 IWritableShaderSource *shsrc,
271 IWritableItemDefManager *itemdef,
272 IWritableNodeDefManager *nodedef,
273 ISoundManager *sound,
274 MtEventManager *event,
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
560 // [3] u8[20] player_name
561 // [23] u8[28] password (new in some version)
562 // [51] u16 minimum supported network protocol version (added sometime)
563 // [53] u16 maximum supported network protocol version (added later than the previous one)
564 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
565 writeU16(&data[0], TOSERVER_INIT);
566 writeU8(&data[2], SER_FMT_VER_HIGHEST);
568 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
569 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
571 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
574 memset((char*)&data[23], 0, PASSWORD_SIZE);
575 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
577 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
578 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
580 // Send as unreliable
581 Send(0, data, false);
584 // Not connected, return
589 Do stuff if connected
593 Run Map's timers and unload unused data
595 const float map_timer_and_unload_dtime = 5.25;
596 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
598 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
599 std::list<v3s16> deleted_blocks;
600 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
601 g_settings->getFloat("client_unload_unused_data_timeout"),
604 /*if(deleted_blocks.size() > 0)
605 infostream<<"Client: Unloaded "<<deleted_blocks.size()
606 <<" unused blocks"<<std::endl;*/
610 NOTE: This loop is intentionally iterated the way it is.
613 std::list<v3s16>::iterator i = deleted_blocks.begin();
614 std::list<v3s16> sendlist;
617 if(sendlist.size() == 255 || i == deleted_blocks.end())
619 if(sendlist.size() == 0)
628 u32 replysize = 2+1+6*sendlist.size();
629 SharedBuffer<u8> reply(replysize);
630 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
631 reply[2] = sendlist.size();
633 for(std::list<v3s16>::iterator
634 j = sendlist.begin();
635 j != sendlist.end(); ++j)
637 writeV3S16(&reply[2+1+6*k], *j);
640 m_con.Send(PEER_ID_SERVER, 1, reply, true);
642 if(i == deleted_blocks.end())
648 sendlist.push_back(*i);
658 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
660 // Control local player (0ms)
661 LocalPlayer *player = m_env.getLocalPlayer();
662 assert(player != NULL);
663 player->applyControl(dtime);
665 //TimeTaker envtimer("env step", m_device);
674 ClientEnvEvent event = m_env.getClientEvent();
675 if(event.type == CEE_NONE)
679 else if(event.type == CEE_PLAYER_DAMAGE)
681 if(m_ignore_damage_timer <= 0)
683 u8 damage = event.player_damage.amount;
685 if(event.player_damage.send_to_server)
688 // Add to ClientEvent queue
690 event.type = CE_PLAYER_DAMAGE;
691 event.player_damage.amount = damage;
692 m_client_event_queue.push_back(event);
702 float &counter = m_avg_rtt_timer;
707 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
708 // connectedAndInitialized() is true, peer exists.
709 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
710 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
715 Send player position to server
718 float &counter = m_playerpos_send_timer;
720 if(counter >= m_recommended_send_interval)
728 Replace updated meshes
731 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
733 //TimeTaker timer("** Processing mesh update result queue");
736 /*infostream<<"Mesh update result queue size is "
737 <<m_mesh_update_thread.m_queue_out.size()
740 int num_processed_meshes = 0;
741 while(!m_mesh_update_thread.m_queue_out.empty())
743 num_processed_meshes++;
744 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
745 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
748 //JMutexAutoLock lock(block->mesh_mutex);
750 // Delete the old mesh
751 if(block->mesh != NULL)
753 // TODO: Remove hardware buffers of meshbuffers of block->mesh
758 // Replace with the new mesh
759 block->mesh = r.mesh;
763 if(r.ack_block_to_server)
765 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
766 <<","<<r.p.Z<<")"<<std::endl;*/
777 u32 replysize = 2+1+6;
778 SharedBuffer<u8> reply(replysize);
779 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
781 writeV3S16(&reply[3], r.p);
783 m_con.Send(PEER_ID_SERVER, 1, reply, true);
786 if(num_processed_meshes > 0)
787 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
793 if (m_media_receive_started) {
794 bool all_stopped = true;
795 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
796 thread != m_media_fetch_threads.end(); ++thread) {
797 all_stopped &= !(*thread)->IsRunning();
798 while (!(*thread)->m_file_data.empty()) {
799 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
800 if(m_media_received_count < m_media_count)
801 m_media_received_count++;
803 bool success = loadMedia(out.second, out.first);
805 verbosestream<<"Client: Loaded received media: "
806 <<"\""<<out.first<<"\". Caching."<<std::endl;
808 infostream<<"Client: Failed to load received media: "
809 <<"\""<<out.first<<"\". Not caching."<<std::endl;
813 bool did = fs::CreateAllDirs(getMediaCacheDir());
815 errorstream<<"Could not create media cache directory"
820 std::map<std::string, std::string>::iterator n;
821 n = m_media_name_sha1_map.find(out.first);
822 if(n == m_media_name_sha1_map.end())
823 errorstream<<"The server sent a file that has not "
824 <<"been announced."<<std::endl;
826 m_media_cache.update_sha1(out.second);
831 std::list<MediaRequest> fetch_failed;
832 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
833 thread != m_media_fetch_threads.end(); ++thread) {
834 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
835 request != (*thread)->m_failed.end(); ++request)
836 fetch_failed.push_back(*request);
837 (*thread)->m_failed.clear();
839 if (fetch_failed.size() > 0) {
840 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
841 << "Requesting them the usual way." << std::endl;
842 request_media(fetch_failed);
848 If the server didn't update the inventory in a while, revert
849 the local inventory (so the player notices the lag problem
850 and knows something is wrong).
852 if(m_inventory_from_server)
854 float interval = 10.0;
855 float count_before = floor(m_inventory_from_server_age / interval);
857 m_inventory_from_server_age += dtime;
859 float count_after = floor(m_inventory_from_server_age / interval);
861 if(count_after != count_before)
863 // Do this every <interval> seconds after TOCLIENT_INVENTORY
864 // Reset the locally changed inventory to the authoritative inventory
865 Player *player = m_env.getLocalPlayer();
866 player->inventory = *m_inventory_from_server;
867 m_inventory_updated = true;
872 Update positions of sounds attached to objects
875 for(std::map<int, u16>::iterator
876 i = m_sounds_to_objects.begin();
877 i != m_sounds_to_objects.end(); i++)
879 int client_id = i->first;
880 u16 object_id = i->second;
881 ClientActiveObject *cao = m_env.getActiveObject(object_id);
884 v3f pos = cao->getPosition();
885 m_sound->updateSoundPosition(client_id, pos);
890 Handle removed remotely initiated sounds
892 m_removed_sounds_check_timer += dtime;
893 if(m_removed_sounds_check_timer >= 2.32)
895 m_removed_sounds_check_timer = 0;
896 // Find removed sounds and clear references to them
897 std::set<s32> removed_server_ids;
898 for(std::map<s32, int>::iterator
899 i = m_sounds_server_to_client.begin();
900 i != m_sounds_server_to_client.end();)
902 s32 server_id = i->first;
903 int client_id = i->second;
905 if(!m_sound->soundExists(client_id)){
906 m_sounds_server_to_client.erase(server_id);
907 m_sounds_client_to_server.erase(client_id);
908 m_sounds_to_objects.erase(client_id);
909 removed_server_ids.insert(server_id);
913 if(removed_server_ids.size() != 0)
915 std::ostringstream os(std::ios_base::binary);
916 writeU16(os, TOSERVER_REMOVED_SOUNDS);
917 writeU16(os, removed_server_ids.size());
918 for(std::set<s32>::iterator i = removed_server_ids.begin();
919 i != removed_server_ids.end(); i++)
921 std::string s = os.str();
922 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
929 bool Client::loadMedia(const std::string &data, const std::string &filename)
931 // Silly irrlicht's const-incorrectness
932 Buffer<char> data_rw(data.c_str(), data.size());
936 const char *image_ext[] = {
937 ".png", ".jpg", ".bmp", ".tga",
938 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
941 name = removeStringEnd(filename, image_ext);
944 verbosestream<<"Client: Attempting to load image "
945 <<"file \""<<filename<<"\""<<std::endl;
947 io::IFileSystem *irrfs = m_device->getFileSystem();
948 video::IVideoDriver *vdrv = m_device->getVideoDriver();
950 // Create an irrlicht memory file
951 io::IReadFile *rfile = irrfs->createMemoryReadFile(
952 *data_rw, data_rw.getSize(), "_tempreadfile");
955 video::IImage *img = vdrv->createImageFromFile(rfile);
957 errorstream<<"Client: Cannot create image from data of "
958 <<"file \""<<filename<<"\""<<std::endl;
963 m_tsrc->insertSourceImage(filename, img);
970 const char *sound_ext[] = {
971 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
972 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
975 name = removeStringEnd(filename, sound_ext);
978 verbosestream<<"Client: Attempting to load sound "
979 <<"file \""<<filename<<"\""<<std::endl;
980 m_sound->loadSoundData(name, data);
984 const char *model_ext[] = {
985 ".x", ".b3d", ".md2", ".obj",
988 name = removeStringEnd(filename, model_ext);
991 verbosestream<<"Client: Storing model into Irrlicht: "
992 <<"\""<<filename<<"\""<<std::endl;
993 scene::ISceneManager *smgr = m_device->getSceneManager();
995 //check if mesh was already cached
996 scene::IAnimatedMesh *mesh =
997 smgr->getMeshCache()->getMeshByName(filename.c_str());
1000 errorstream << "Multiple models with name: " << filename.c_str() <<
1001 " found replacing previous model!" << std::endl;
1003 smgr->getMeshCache()->removeMesh(mesh);
1007 io::IFileSystem *irrfs = m_device->getFileSystem();
1008 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1009 *data_rw, data_rw.getSize(), filename.c_str());
1012 mesh = smgr->getMesh(rfile);
1013 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1018 errorstream<<"Client: Don't know how to load file \""
1019 <<filename<<"\""<<std::endl;
1023 // Virtual methods from con::PeerHandler
1024 void Client::peerAdded(con::Peer *peer)
1026 infostream<<"Client::peerAdded(): peer->id="
1027 <<peer->id<<std::endl;
1029 void Client::deletingPeer(con::Peer *peer, bool timeout)
1031 infostream<<"Client::deletingPeer(): "
1032 "Server Peer is getting deleted "
1033 <<"(timeout="<<timeout<<")"<<std::endl;
1038 u16 number of files requested
1044 void Client::request_media(const std::list<MediaRequest> &file_requests)
1046 std::ostringstream os(std::ios_base::binary);
1047 writeU16(os, TOSERVER_REQUEST_MEDIA);
1048 writeU16(os, file_requests.size());
1050 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1051 i != file_requests.end(); ++i) {
1052 os<<serializeString(i->name);
1056 std::string s = os.str();
1057 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1059 Send(0, data, true);
1060 infostream<<"Client: Sending media request list to server ("
1061 <<file_requests.size()<<" files)"<<std::endl;
1064 void Client::ReceiveAll()
1066 DSTACK(__FUNCTION_NAME);
1067 u32 start_ms = porting::getTimeMs();
1070 // Limit time even if there would be huge amounts of data to
1072 if(porting::getTimeMs() > start_ms + 100)
1077 g_profiler->graphAdd("client_received_packets", 1);
1079 catch(con::NoIncomingDataException &e)
1083 catch(con::InvalidIncomingDataException &e)
1085 infostream<<"Client::ReceiveAll(): "
1086 "InvalidIncomingDataException: what()="
1087 <<e.what()<<std::endl;
1092 void Client::Receive()
1094 DSTACK(__FUNCTION_NAME);
1095 SharedBuffer<u8> data;
1099 //TimeTaker t1("con mutex and receive", m_device);
1100 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1101 datasize = m_con.Receive(sender_peer_id, data);
1103 //TimeTaker t1("ProcessData", m_device);
1104 ProcessData(*data, datasize, sender_peer_id);
1108 sender_peer_id given to this shall be quaranteed to be a valid peer
1110 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1112 DSTACK(__FUNCTION_NAME);
1114 // Ignore packets that don't even fit a command
1117 m_packetcounter.add(60000);
1121 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1123 //infostream<<"Client: received command="<<command<<std::endl;
1124 m_packetcounter.add((u16)command);
1127 If this check is removed, be sure to change the queue
1128 system to know the ids
1130 if(sender_peer_id != PEER_ID_SERVER)
1132 infostream<<"Client::ProcessData(): Discarding data not "
1133 "coming from server: peer_id="<<sender_peer_id
1138 u8 ser_version = m_server_ser_ver;
1140 //infostream<<"Client received command="<<(int)command<<std::endl;
1142 if(command == TOCLIENT_INIT)
1147 u8 deployed = data[2];
1149 infostream<<"Client: TOCLIENT_INIT received with "
1150 "deployed="<<((int)deployed&0xff)<<std::endl;
1152 if(deployed < SER_FMT_VER_LOWEST
1153 || deployed > SER_FMT_VER_HIGHEST)
1155 infostream<<"Client: TOCLIENT_INIT: Server sent "
1156 <<"unsupported ser_fmt_ver"<<std::endl;
1160 m_server_ser_ver = deployed;
1162 // Get player position
1163 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1164 if(datasize >= 2+1+6)
1165 playerpos_s16 = readV3S16(&data[2+1]);
1166 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1169 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1171 // Set player position
1172 Player *player = m_env.getLocalPlayer();
1173 assert(player != NULL);
1174 player->setPosition(playerpos_f);
1177 if(datasize >= 2+1+6+8)
1180 m_map_seed = readU64(&data[2+1+6]);
1181 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1184 if(datasize >= 2+1+6+8+4)
1187 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1188 infostream<<"Client: received recommended send interval "
1189 <<m_recommended_send_interval<<std::endl;
1194 SharedBuffer<u8> reply(replysize);
1195 writeU16(&reply[0], TOSERVER_INIT2);
1197 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1202 if(command == TOCLIENT_ACCESS_DENIED)
1204 // The server didn't like our password. Note, this needs
1205 // to be processed even if the serialisation format has
1206 // not been agreed yet, the same as TOCLIENT_INIT.
1207 m_access_denied = true;
1208 m_access_denied_reason = L"Unknown";
1211 std::string datastring((char*)&data[2], datasize-2);
1212 std::istringstream is(datastring, std::ios_base::binary);
1213 m_access_denied_reason = deSerializeWideString(is);
1218 if(ser_version == SER_FMT_VER_INVALID)
1220 infostream<<"Client: Server serialization"
1221 " format invalid or not initialized."
1222 " Skipping incoming command="<<command<<std::endl;
1226 // Just here to avoid putting the two if's together when
1227 // making some copypasta
1230 if(command == TOCLIENT_REMOVENODE)
1235 p.X = readS16(&data[2]);
1236 p.Y = readS16(&data[4]);
1237 p.Z = readS16(&data[6]);
1239 //TimeTaker t1("TOCLIENT_REMOVENODE");
1243 else if(command == TOCLIENT_ADDNODE)
1245 if(datasize < 8 + MapNode::serializedLength(ser_version))
1249 p.X = readS16(&data[2]);
1250 p.Y = readS16(&data[4]);
1251 p.Z = readS16(&data[6]);
1253 //TimeTaker t1("TOCLIENT_ADDNODE");
1256 n.deSerialize(&data[8], ser_version);
1260 else if(command == TOCLIENT_BLOCKDATA)
1262 // Ignore too small packet
1267 p.X = readS16(&data[2]);
1268 p.Y = readS16(&data[4]);
1269 p.Z = readS16(&data[6]);
1271 /*infostream<<"Client: Thread: BLOCKDATA for ("
1272 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1273 /*infostream<<"Client: Thread: BLOCKDATA for ("
1274 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1276 std::string datastring((char*)&data[8], datasize-8);
1277 std::istringstream istr(datastring, std::ios_base::binary);
1282 v2s16 p2d(p.X, p.Z);
1283 sector = m_env.getMap().emergeSector(p2d);
1285 assert(sector->getPos() == p2d);
1287 //TimeTaker timer("MapBlock deSerialize");
1290 block = sector->getBlockNoCreateNoEx(p.Y);
1294 Update an existing block
1296 //infostream<<"Updating"<<std::endl;
1297 block->deSerialize(istr, ser_version, false);
1304 //infostream<<"Creating new"<<std::endl;
1305 block = new MapBlock(&m_env.getMap(), p, this);
1306 block->deSerialize(istr, ser_version, false);
1307 sector->insertBlock(block);
1321 u32 replysize = 2+1+6;
1322 SharedBuffer<u8> reply(replysize);
1323 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1325 writeV3S16(&reply[3], p);
1327 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1331 Add it to mesh update queue and set it to be acknowledged after update.
1333 //infostream<<"Adding mesh update task for received block"<<std::endl;
1334 addUpdateMeshTaskWithEdge(p, true);
1336 else if(command == TOCLIENT_INVENTORY)
1341 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1344 //TimeTaker t2("mutex locking", m_device);
1345 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1348 //TimeTaker t3("istringstream init", m_device);
1349 std::string datastring((char*)&data[2], datasize-2);
1350 std::istringstream is(datastring, std::ios_base::binary);
1353 //m_env.printPlayers(infostream);
1355 //TimeTaker t4("player get", m_device);
1356 Player *player = m_env.getLocalPlayer();
1357 assert(player != NULL);
1360 //TimeTaker t1("inventory.deSerialize()", m_device);
1361 player->inventory.deSerialize(is);
1364 m_inventory_updated = true;
1366 delete m_inventory_from_server;
1367 m_inventory_from_server = new Inventory(player->inventory);
1368 m_inventory_from_server_age = 0.0;
1370 //infostream<<"Client got player inventory:"<<std::endl;
1371 //player->inventory.print(infostream);
1374 else if(command == TOCLIENT_TIME_OF_DAY)
1379 u16 time_of_day = readU16(&data[2]);
1380 time_of_day = time_of_day % 24000;
1381 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1382 float time_speed = 0;
1383 if(datasize >= 2 + 2 + 4){
1384 time_speed = readF1000(&data[4]);
1386 // Old message; try to approximate speed of time by ourselves
1387 float time_of_day_f = (float)time_of_day / 24000.0;
1388 float tod_diff_f = 0;
1389 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1390 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1392 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1393 m_last_time_of_day_f = time_of_day_f;
1394 float time_diff = m_time_of_day_update_timer;
1395 m_time_of_day_update_timer = 0;
1396 if(m_time_of_day_set){
1397 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1398 infostream<<"Client: Measured time_of_day speed (old format): "
1399 <<time_speed<<" tod_diff_f="<<tod_diff_f
1400 <<" time_diff="<<time_diff<<std::endl;
1404 // Update environment
1405 m_env.setTimeOfDay(time_of_day);
1406 m_env.setTimeOfDaySpeed(time_speed);
1407 m_time_of_day_set = true;
1409 u32 dr = m_env.getDayNightRatio();
1410 verbosestream<<"Client: time_of_day="<<time_of_day
1411 <<" time_speed="<<time_speed
1412 <<" dr="<<dr<<std::endl;
1414 else if(command == TOCLIENT_CHAT_MESSAGE)
1422 std::string datastring((char*)&data[2], datasize-2);
1423 std::istringstream is(datastring, std::ios_base::binary);
1426 is.read((char*)buf, 2);
1427 u16 len = readU16(buf);
1429 std::wstring message;
1430 for(u16 i=0; i<len; i++)
1432 is.read((char*)buf, 2);
1433 message += (wchar_t)readU16(buf);
1436 /*infostream<<"Client received chat message: "
1437 <<wide_to_narrow(message)<<std::endl;*/
1439 m_chat_queue.push_back(message);
1441 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1443 //if(g_settings->getBool("enable_experimental"))
1447 u16 count of removed objects
1448 for all removed objects {
1451 u16 count of added objects
1452 for all added objects {
1455 u32 initialization data length
1456 string initialization data
1461 // Get all data except the command number
1462 std::string datastring((char*)&data[2], datasize-2);
1463 // Throw them in an istringstream
1464 std::istringstream is(datastring, std::ios_base::binary);
1468 // Read removed objects
1470 u16 removed_count = readU16((u8*)buf);
1471 for(u16 i=0; i<removed_count; i++)
1474 u16 id = readU16((u8*)buf);
1477 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1478 m_env.removeActiveObject(id);
1482 // Read added objects
1484 u16 added_count = readU16((u8*)buf);
1485 for(u16 i=0; i<added_count; i++)
1488 u16 id = readU16((u8*)buf);
1490 u8 type = readU8((u8*)buf);
1491 std::string data = deSerializeLongString(is);
1494 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1495 m_env.addActiveObject(id, type, data);
1500 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1502 //if(g_settings->getBool("enable_experimental"))
1514 // Get all data except the command number
1515 std::string datastring((char*)&data[2], datasize-2);
1516 // Throw them in an istringstream
1517 std::istringstream is(datastring, std::ios_base::binary);
1519 while(is.eof() == false)
1523 u16 id = readU16((u8*)buf);
1527 u16 message_size = readU16((u8*)buf);
1528 std::string message;
1529 message.reserve(message_size);
1530 for(u16 i=0; i<message_size; i++)
1533 message.append(buf, 1);
1535 // Pass on to the environment
1537 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1538 m_env.processActiveObjectMessage(id, message);
1543 else if(command == TOCLIENT_MOVEMENT)
1545 std::string datastring((char*)&data[2], datasize-2);
1546 std::istringstream is(datastring, std::ios_base::binary);
1547 Player *player = m_env.getLocalPlayer();
1548 assert(player != NULL);
1550 player->movement_acceleration_default = readF1000(is) * BS;
1551 player->movement_acceleration_air = readF1000(is) * BS;
1552 player->movement_acceleration_fast = readF1000(is) * BS;
1553 player->movement_speed_walk = readF1000(is) * BS;
1554 player->movement_speed_crouch = readF1000(is) * BS;
1555 player->movement_speed_fast = readF1000(is) * BS;
1556 player->movement_speed_climb = readF1000(is) * BS;
1557 player->movement_speed_jump = readF1000(is) * BS;
1558 player->movement_liquid_fluidity = readF1000(is) * BS;
1559 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1560 player->movement_liquid_sink = readF1000(is) * BS;
1561 player->movement_gravity = readF1000(is) * BS;
1563 else if(command == TOCLIENT_HP)
1565 std::string datastring((char*)&data[2], datasize-2);
1566 std::istringstream is(datastring, std::ios_base::binary);
1567 Player *player = m_env.getLocalPlayer();
1568 assert(player != NULL);
1569 u8 oldhp = player->hp;
1575 // Add to ClientEvent queue
1577 event.type = CE_PLAYER_DAMAGE;
1578 event.player_damage.amount = oldhp - hp;
1579 m_client_event_queue.push_back(event);
1582 else if(command == TOCLIENT_MOVE_PLAYER)
1584 std::string datastring((char*)&data[2], datasize-2);
1585 std::istringstream is(datastring, std::ios_base::binary);
1586 Player *player = m_env.getLocalPlayer();
1587 assert(player != NULL);
1588 v3f pos = readV3F1000(is);
1589 f32 pitch = readF1000(is);
1590 f32 yaw = readF1000(is);
1591 player->setPosition(pos);
1592 /*player->setPitch(pitch);
1593 player->setYaw(yaw);*/
1595 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1596 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1602 Add to ClientEvent queue.
1603 This has to be sent to the main program because otherwise
1604 it would just force the pitch and yaw values to whatever
1605 the camera points to.
1608 event.type = CE_PLAYER_FORCE_MOVE;
1609 event.player_force_move.pitch = pitch;
1610 event.player_force_move.yaw = yaw;
1611 m_client_event_queue.push_back(event);
1613 // Ignore damage for a few seconds, so that the player doesn't
1614 // get damage from falling on ground
1615 m_ignore_damage_timer = 3.0;
1617 else if(command == TOCLIENT_PLAYERITEM)
1619 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1621 else if(command == TOCLIENT_DEATHSCREEN)
1623 std::string datastring((char*)&data[2], datasize-2);
1624 std::istringstream is(datastring, std::ios_base::binary);
1626 bool set_camera_point_target = readU8(is);
1627 v3f camera_point_target = readV3F1000(is);
1630 event.type = CE_DEATHSCREEN;
1631 event.deathscreen.set_camera_point_target = set_camera_point_target;
1632 event.deathscreen.camera_point_target_x = camera_point_target.X;
1633 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1634 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1635 m_client_event_queue.push_back(event);
1637 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1639 std::string datastring((char*)&data[2], datasize-2);
1640 std::istringstream is(datastring, std::ios_base::binary);
1642 // Mesh update thread must be stopped while
1643 // updating content definitions
1644 assert(!m_mesh_update_thread.IsRunning());
1646 int num_files = readU16(is);
1648 infostream<<"Client: Received media announcement: packet size: "
1649 <<datasize<<std::endl;
1651 std::list<MediaRequest> file_requests;
1653 for(int i=0; i<num_files; i++)
1655 //read file from cache
1656 std::string name = deSerializeString(is);
1657 std::string sha1_base64 = deSerializeString(is);
1659 // if name contains illegal characters, ignore the file
1660 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1661 errorstream<<"Client: ignoring illegal file name "
1662 <<"sent by server: \""<<name<<"\""<<std::endl;
1666 std::string sha1_raw = base64_decode(sha1_base64);
1667 std::string sha1_hex = hex_encode(sha1_raw);
1668 std::ostringstream tmp_os(std::ios_base::binary);
1669 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1670 m_media_name_sha1_map[name] = sha1_raw;
1672 // If found in cache, try to load it from there
1675 bool success = loadMedia(tmp_os.str(), name);
1677 verbosestream<<"Client: Loaded cached media: "
1678 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1681 infostream<<"Client: Failed to load cached media: "
1682 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1685 // Didn't load from cache; queue it to be requested
1686 verbosestream<<"Client: Adding file to request list: \""
1687 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1688 file_requests.push_back(MediaRequest(name));
1691 std::string remote_media = "";
1693 remote_media = deSerializeString(is);
1695 catch(SerializationError) {
1696 // not supported by server or turned off
1699 m_media_count = file_requests.size();
1700 m_media_receive_started = true;
1702 if (remote_media == "" || !USE_CURL) {
1703 request_media(file_requests);
1706 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1707 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1708 i != file_requests.end(); ++i) {
1709 (*cur)->m_file_requests.push_back(*i);
1711 if (cur == m_media_fetch_threads.end())
1712 cur = m_media_fetch_threads.begin();
1714 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1715 i != m_media_fetch_threads.end(); ++i) {
1716 (*i)->m_remote_url = remote_media;
1721 // notify server we received everything
1722 std::ostringstream os(std::ios_base::binary);
1723 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1724 std::string s = os.str();
1725 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1727 Send(0, data, true);
1730 event.type = CE_TEXTURES_UPDATED;
1731 m_client_event_queue.push_back(event);
1733 else if(command == TOCLIENT_MEDIA)
1735 std::string datastring((char*)&data[2], datasize-2);
1736 std::istringstream is(datastring, std::ios_base::binary);
1740 u16 total number of file bunches
1741 u16 index of this bunch
1742 u32 number of files in this bunch
1750 int num_bunches = readU16(is);
1751 int bunch_i = readU16(is);
1752 u32 num_files = readU32(is);
1753 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1754 <<num_bunches<<" files="<<num_files
1755 <<" size="<<datasize<<std::endl;
1757 // Check total and received media count
1758 assert(m_media_received_count <= m_media_count);
1759 if (num_files > m_media_count - m_media_received_count) {
1760 errorstream<<"Client: Received more files than requested:"
1761 <<" total count="<<m_media_count
1762 <<" total received="<<m_media_received_count
1763 <<" bunch "<<bunch_i<<"/"<<num_bunches
1764 <<" files="<<num_files
1765 <<" size="<<datasize<<std::endl;
1766 num_files = m_media_count - m_media_received_count;
1771 // Mesh update thread must be stopped while
1772 // updating content definitions
1773 assert(!m_mesh_update_thread.IsRunning());
1775 for(u32 i=0; i<num_files; i++){
1776 assert(m_media_received_count < m_media_count);
1777 m_media_received_count++;
1778 std::string name = deSerializeString(is);
1779 std::string data = deSerializeLongString(is);
1781 // if name contains illegal characters, ignore the file
1782 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1783 errorstream<<"Client: ignoring illegal file name "
1784 <<"sent by server: \""<<name<<"\""<<std::endl;
1788 bool success = loadMedia(data, name);
1790 verbosestream<<"Client: Loaded received media: "
1791 <<"\""<<name<<"\". Caching."<<std::endl;
1793 infostream<<"Client: Failed to load received media: "
1794 <<"\""<<name<<"\". Not caching."<<std::endl;
1798 bool did = fs::CreateAllDirs(getMediaCacheDir());
1800 errorstream<<"Could not create media cache directory"
1805 std::map<std::string, std::string>::iterator n;
1806 n = m_media_name_sha1_map.find(name);
1807 if(n == m_media_name_sha1_map.end())
1808 errorstream<<"The server sent a file that has not "
1809 <<"been announced."<<std::endl;
1811 m_media_cache.update_sha1(data);
1816 event.type = CE_TEXTURES_UPDATED;
1817 m_client_event_queue.push_back(event);
1819 else if(command == TOCLIENT_TOOLDEF)
1821 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1823 else if(command == TOCLIENT_NODEDEF)
1825 infostream<<"Client: Received node definitions: packet size: "
1826 <<datasize<<std::endl;
1828 // Mesh update thread must be stopped while
1829 // updating content definitions
1830 assert(!m_mesh_update_thread.IsRunning());
1832 // Decompress node definitions
1833 std::string datastring((char*)&data[2], datasize-2);
1834 std::istringstream is(datastring, std::ios_base::binary);
1835 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1836 std::ostringstream tmp_os;
1837 decompressZlib(tmp_is, tmp_os);
1839 // Deserialize node definitions
1840 std::istringstream tmp_is2(tmp_os.str());
1841 m_nodedef->deSerialize(tmp_is2);
1842 m_nodedef_received = true;
1844 else if(command == TOCLIENT_CRAFTITEMDEF)
1846 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1848 else if(command == TOCLIENT_ITEMDEF)
1850 infostream<<"Client: Received item definitions: packet size: "
1851 <<datasize<<std::endl;
1853 // Mesh update thread must be stopped while
1854 // updating content definitions
1855 assert(!m_mesh_update_thread.IsRunning());
1857 // Decompress item definitions
1858 std::string datastring((char*)&data[2], datasize-2);
1859 std::istringstream is(datastring, std::ios_base::binary);
1860 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1861 std::ostringstream tmp_os;
1862 decompressZlib(tmp_is, tmp_os);
1864 // Deserialize node definitions
1865 std::istringstream tmp_is2(tmp_os.str());
1866 m_itemdef->deSerialize(tmp_is2);
1867 m_itemdef_received = true;
1869 else if(command == TOCLIENT_PLAY_SOUND)
1871 std::string datastring((char*)&data[2], datasize-2);
1872 std::istringstream is(datastring, std::ios_base::binary);
1874 s32 server_id = readS32(is);
1875 std::string name = deSerializeString(is);
1876 float gain = readF1000(is);
1877 int type = readU8(is); // 0=local, 1=positional, 2=object
1878 v3f pos = readV3F1000(is);
1879 u16 object_id = readU16(is);
1880 bool loop = readU8(is);
1885 client_id = m_sound->playSound(name, loop, gain);
1887 case 1: // positional
1888 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1891 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1893 pos = cao->getPosition();
1894 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1895 // TODO: Set up sound to move with object
1900 if(client_id != -1){
1901 m_sounds_server_to_client[server_id] = client_id;
1902 m_sounds_client_to_server[client_id] = server_id;
1904 m_sounds_to_objects[client_id] = object_id;
1907 else if(command == TOCLIENT_STOP_SOUND)
1909 std::string datastring((char*)&data[2], datasize-2);
1910 std::istringstream is(datastring, std::ios_base::binary);
1912 s32 server_id = readS32(is);
1913 std::map<s32, int>::iterator i =
1914 m_sounds_server_to_client.find(server_id);
1915 if(i != m_sounds_server_to_client.end()){
1916 int client_id = i->second;
1917 m_sound->stopSound(client_id);
1920 else if(command == TOCLIENT_PRIVILEGES)
1922 std::string datastring((char*)&data[2], datasize-2);
1923 std::istringstream is(datastring, std::ios_base::binary);
1925 m_privileges.clear();
1926 infostream<<"Client: Privileges updated: ";
1927 u16 num_privileges = readU16(is);
1928 for(u16 i=0; i<num_privileges; i++){
1929 std::string priv = deSerializeString(is);
1930 m_privileges.insert(priv);
1931 infostream<<priv<<" ";
1933 infostream<<std::endl;
1935 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1937 std::string datastring((char*)&data[2], datasize-2);
1938 std::istringstream is(datastring, std::ios_base::binary);
1940 // Store formspec in LocalPlayer
1941 Player *player = m_env.getLocalPlayer();
1942 assert(player != NULL);
1943 player->inventory_formspec = deSerializeLongString(is);
1945 else if(command == TOCLIENT_DETACHED_INVENTORY)
1947 std::string datastring((char*)&data[2], datasize-2);
1948 std::istringstream is(datastring, std::ios_base::binary);
1950 std::string name = deSerializeString(is);
1952 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1954 Inventory *inv = NULL;
1955 if(m_detached_inventories.count(name) > 0)
1956 inv = m_detached_inventories[name];
1958 inv = new Inventory(m_itemdef);
1959 m_detached_inventories[name] = inv;
1961 inv->deSerialize(is);
1963 else if(command == TOCLIENT_SHOW_FORMSPEC)
1965 std::string datastring((char*)&data[2], datasize-2);
1966 std::istringstream is(datastring, std::ios_base::binary);
1968 std::string formspec = deSerializeLongString(is);
1969 std::string formname = deSerializeString(is);
1972 event.type = CE_SHOW_FORMSPEC;
1973 // pointer is required as event is a struct only!
1974 // adding a std:string to a struct isn't possible
1975 event.show_formspec.formspec = new std::string(formspec);
1976 event.show_formspec.formname = new std::string(formname);
1977 m_client_event_queue.push_back(event);
1979 else if(command == TOCLIENT_SPAWN_PARTICLE)
1981 std::string datastring((char*)&data[2], datasize-2);
1982 std::istringstream is(datastring, std::ios_base::binary);
1984 v3f pos = readV3F1000(is);
1985 v3f vel = readV3F1000(is);
1986 v3f acc = readV3F1000(is);
1987 float expirationtime = readF1000(is);
1988 float size = readF1000(is);
1989 bool collisiondetection = readU8(is);
1990 std::string texture = deSerializeLongString(is);
1993 event.type = CE_SPAWN_PARTICLE;
1994 event.spawn_particle.pos = new v3f (pos);
1995 event.spawn_particle.vel = new v3f (vel);
1996 event.spawn_particle.acc = new v3f (acc);
1998 event.spawn_particle.expirationtime = expirationtime;
1999 event.spawn_particle.size = size;
2000 event.spawn_particle.collisiondetection =
2002 event.spawn_particle.texture = new std::string(texture);
2004 m_client_event_queue.push_back(event);
2006 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2008 std::string datastring((char*)&data[2], datasize-2);
2009 std::istringstream is(datastring, std::ios_base::binary);
2011 u16 amount = readU16(is);
2012 float spawntime = readF1000(is);
2013 v3f minpos = readV3F1000(is);
2014 v3f maxpos = readV3F1000(is);
2015 v3f minvel = readV3F1000(is);
2016 v3f maxvel = readV3F1000(is);
2017 v3f minacc = readV3F1000(is);
2018 v3f maxacc = readV3F1000(is);
2019 float minexptime = readF1000(is);
2020 float maxexptime = readF1000(is);
2021 float minsize = readF1000(is);
2022 float maxsize = readF1000(is);
2023 bool collisiondetection = readU8(is);
2024 std::string texture = deSerializeLongString(is);
2025 u32 id = readU32(is);
2028 event.type = CE_ADD_PARTICLESPAWNER;
2029 event.add_particlespawner.amount = amount;
2030 event.add_particlespawner.spawntime = spawntime;
2032 event.add_particlespawner.minpos = new v3f (minpos);
2033 event.add_particlespawner.maxpos = new v3f (maxpos);
2034 event.add_particlespawner.minvel = new v3f (minvel);
2035 event.add_particlespawner.maxvel = new v3f (maxvel);
2036 event.add_particlespawner.minacc = new v3f (minacc);
2037 event.add_particlespawner.maxacc = new v3f (maxacc);
2039 event.add_particlespawner.minexptime = minexptime;
2040 event.add_particlespawner.maxexptime = maxexptime;
2041 event.add_particlespawner.minsize = minsize;
2042 event.add_particlespawner.maxsize = maxsize;
2043 event.add_particlespawner.collisiondetection = collisiondetection;
2044 event.add_particlespawner.texture = new std::string(texture);
2045 event.add_particlespawner.id = id;
2047 m_client_event_queue.push_back(event);
2049 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2051 std::string datastring((char*)&data[2], datasize-2);
2052 std::istringstream is(datastring, std::ios_base::binary);
2054 u32 id = readU16(is);
2057 event.type = CE_DELETE_PARTICLESPAWNER;
2058 event.delete_particlespawner.id = id;
2060 m_client_event_queue.push_back(event);
2062 else if(command == TOCLIENT_HUDADD)
2064 std::string datastring((char *)&data[2], datasize - 2);
2065 std::istringstream is(datastring, std::ios_base::binary);
2067 u32 id = readU32(is);
2068 u8 type = readU8(is);
2069 v2f pos = readV2F1000(is);
2070 std::string name = deSerializeString(is);
2071 v2f scale = readV2F1000(is);
2072 std::string text = deSerializeString(is);
2073 u32 number = readU32(is);
2074 u32 item = readU32(is);
2075 u32 dir = readU32(is);
2076 v2f align = readV2F1000(is);
2077 v2f offset = readV2F1000(is);
2080 event.type = CE_HUDADD;
2081 event.hudadd.id = id;
2082 event.hudadd.type = type;
2083 event.hudadd.pos = new v2f(pos);
2084 event.hudadd.name = new std::string(name);
2085 event.hudadd.scale = new v2f(scale);
2086 event.hudadd.text = new std::string(text);
2087 event.hudadd.number = number;
2088 event.hudadd.item = item;
2089 event.hudadd.dir = dir;
2090 event.hudadd.align = new v2f(align);
2091 event.hudadd.offset = new v2f(offset);
2092 m_client_event_queue.push_back(event);
2094 else if(command == TOCLIENT_HUDRM)
2096 std::string datastring((char *)&data[2], datasize - 2);
2097 std::istringstream is(datastring, std::ios_base::binary);
2099 u32 id = readU32(is);
2102 event.type = CE_HUDRM;
2103 event.hudrm.id = id;
2104 m_client_event_queue.push_back(event);
2106 else if(command == TOCLIENT_HUDCHANGE)
2112 std::string datastring((char *)&data[2], datasize - 2);
2113 std::istringstream is(datastring, std::ios_base::binary);
2115 u32 id = readU32(is);
2116 u8 stat = (HudElementStat)readU8(is);
2118 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2119 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2120 v2fdata = readV2F1000(is);
2121 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2122 sdata = deSerializeString(is);
2124 intdata = readU32(is);
2127 event.type = CE_HUDCHANGE;
2128 event.hudchange.id = id;
2129 event.hudchange.stat = (HudElementStat)stat;
2130 event.hudchange.v2fdata = new v2f(v2fdata);
2131 event.hudchange.sdata = new std::string(sdata);
2132 event.hudchange.data = intdata;
2133 m_client_event_queue.push_back(event);
2135 else if(command == TOCLIENT_HUD_SET_FLAGS)
2137 std::string datastring((char *)&data[2], datasize - 2);
2138 std::istringstream is(datastring, std::ios_base::binary);
2140 Player *player = m_env.getLocalPlayer();
2141 assert(player != NULL);
2143 u32 flags = readU32(is);
2144 u32 mask = readU32(is);
2146 player->hud_flags &= ~mask;
2147 player->hud_flags |= flags;
2149 else if(command == TOCLIENT_HUD_SET_PARAM)
2151 std::string datastring((char *)&data[2], datasize - 2);
2152 std::istringstream is(datastring, std::ios_base::binary);
2154 Player *player = m_env.getLocalPlayer();
2155 assert(player != NULL);
2157 u16 param = readU16(is);
2158 std::string value = deSerializeString(is);
2160 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2161 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2162 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2163 player->hud_hotbar_itemcount = hotbar_itemcount;
2168 infostream<<"Client: Ignoring unknown command "
2169 <<command<<std::endl;
2173 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2175 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2176 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2179 void Client::interact(u8 action, const PointedThing& pointed)
2181 if(connectedAndInitialized() == false){
2182 infostream<<"Client::interact() "
2183 "cancelled (not connected)"
2188 std::ostringstream os(std::ios_base::binary);
2194 [5] u32 length of the next item
2195 [9] serialized PointedThing
2197 0: start digging (from undersurface) or use
2198 1: stop digging (all parameters ignored)
2199 2: digging completed
2200 3: place block or item (to abovesurface)
2203 writeU16(os, TOSERVER_INTERACT);
2204 writeU8(os, action);
2205 writeU16(os, getPlayerItem());
2206 std::ostringstream tmp_os(std::ios::binary);
2207 pointed.serialize(tmp_os);
2208 os<<serializeLongString(tmp_os.str());
2210 std::string s = os.str();
2211 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2214 Send(0, data, true);
2217 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2218 const std::map<std::string, std::string> &fields)
2220 std::ostringstream os(std::ios_base::binary);
2222 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2224 os<<serializeString(formname);
2225 writeU16(os, fields.size());
2226 for(std::map<std::string, std::string>::const_iterator
2227 i = fields.begin(); i != fields.end(); i++){
2228 const std::string &name = i->first;
2229 const std::string &value = i->second;
2230 os<<serializeString(name);
2231 os<<serializeLongString(value);
2235 std::string s = os.str();
2236 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2238 Send(0, data, true);
2241 void Client::sendInventoryFields(const std::string &formname,
2242 const std::map<std::string, std::string> &fields)
2244 std::ostringstream os(std::ios_base::binary);
2246 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2247 os<<serializeString(formname);
2248 writeU16(os, fields.size());
2249 for(std::map<std::string, std::string>::const_iterator
2250 i = fields.begin(); i != fields.end(); i++){
2251 const std::string &name = i->first;
2252 const std::string &value = i->second;
2253 os<<serializeString(name);
2254 os<<serializeLongString(value);
2258 std::string s = os.str();
2259 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2261 Send(0, data, true);
2264 void Client::sendInventoryAction(InventoryAction *a)
2266 std::ostringstream os(std::ios_base::binary);
2270 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2271 os.write((char*)buf, 2);
2276 std::string s = os.str();
2277 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2279 Send(0, data, true);
2282 void Client::sendChatMessage(const std::wstring &message)
2284 std::ostringstream os(std::ios_base::binary);
2288 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2289 os.write((char*)buf, 2);
2292 writeU16(buf, message.size());
2293 os.write((char*)buf, 2);
2296 for(u32 i=0; i<message.size(); i++)
2300 os.write((char*)buf, 2);
2304 std::string s = os.str();
2305 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2307 Send(0, data, true);
2310 void Client::sendChangePassword(const std::wstring oldpassword,
2311 const std::wstring newpassword)
2313 Player *player = m_env.getLocalPlayer();
2317 std::string playername = player->getName();
2318 std::string oldpwd = translatePassword(playername, oldpassword);
2319 std::string newpwd = translatePassword(playername, newpassword);
2321 std::ostringstream os(std::ios_base::binary);
2322 u8 buf[2+PASSWORD_SIZE*2];
2324 [0] u16 TOSERVER_PASSWORD
2325 [2] u8[28] old password
2326 [30] u8[28] new password
2329 writeU16(buf, TOSERVER_PASSWORD);
2330 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2332 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2333 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2335 buf[2+PASSWORD_SIZE-1] = 0;
2336 buf[30+PASSWORD_SIZE-1] = 0;
2337 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2340 std::string s = os.str();
2341 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2343 Send(0, data, true);
2347 void Client::sendDamage(u8 damage)
2349 DSTACK(__FUNCTION_NAME);
2350 std::ostringstream os(std::ios_base::binary);
2352 writeU16(os, TOSERVER_DAMAGE);
2353 writeU8(os, damage);
2356 std::string s = os.str();
2357 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2359 Send(0, data, true);
2362 void Client::sendRespawn()
2364 DSTACK(__FUNCTION_NAME);
2365 std::ostringstream os(std::ios_base::binary);
2367 writeU16(os, TOSERVER_RESPAWN);
2370 std::string s = os.str();
2371 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2373 Send(0, data, true);
2376 void Client::sendPlayerPos()
2378 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2380 LocalPlayer *myplayer = m_env.getLocalPlayer();
2381 if(myplayer == NULL)
2384 // Save bandwidth by only updating position when something changed
2385 if(myplayer->last_position == myplayer->getPosition() &&
2386 myplayer->last_speed == myplayer->getSpeed() &&
2387 myplayer->last_pitch == myplayer->getPitch() &&
2388 myplayer->last_yaw == myplayer->getYaw() &&
2389 myplayer->last_keyPressed == myplayer->keyPressed)
2392 myplayer->last_position = myplayer->getPosition();
2393 myplayer->last_speed = myplayer->getSpeed();
2394 myplayer->last_pitch = myplayer->getPitch();
2395 myplayer->last_yaw = myplayer->getYaw();
2396 myplayer->last_keyPressed = myplayer->keyPressed;
2400 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2401 our_peer_id = m_con.GetPeerID();
2404 // Set peer id if not set already
2405 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2406 myplayer->peer_id = our_peer_id;
2407 // Check that an existing peer_id is the same as the connection's
2408 assert(myplayer->peer_id == our_peer_id);
2410 v3f pf = myplayer->getPosition();
2411 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2412 v3f sf = myplayer->getSpeed();
2413 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2414 s32 pitch = myplayer->getPitch() * 100;
2415 s32 yaw = myplayer->getYaw() * 100;
2416 u32 keyPressed=myplayer->keyPressed;
2420 [2] v3s32 position*100
2421 [2+12] v3s32 speed*100
2422 [2+12+12] s32 pitch*100
2423 [2+12+12+4] s32 yaw*100
2424 [2+12+12+4+4] u32 keyPressed
2426 SharedBuffer<u8> data(2+12+12+4+4+4);
2427 writeU16(&data[0], TOSERVER_PLAYERPOS);
2428 writeV3S32(&data[2], position);
2429 writeV3S32(&data[2+12], speed);
2430 writeS32(&data[2+12+12], pitch);
2431 writeS32(&data[2+12+12+4], yaw);
2432 writeU32(&data[2+12+12+4+4], keyPressed);
2433 // Send as unreliable
2434 Send(0, data, false);
2437 void Client::sendPlayerItem(u16 item)
2439 Player *myplayer = m_env.getLocalPlayer();
2440 if(myplayer == NULL)
2443 u16 our_peer_id = m_con.GetPeerID();
2445 // Set peer id if not set already
2446 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2447 myplayer->peer_id = our_peer_id;
2448 // Check that an existing peer_id is the same as the connection's
2449 assert(myplayer->peer_id == our_peer_id);
2451 SharedBuffer<u8> data(2+2);
2452 writeU16(&data[0], TOSERVER_PLAYERITEM);
2453 writeU16(&data[2], item);
2456 Send(0, data, true);
2459 void Client::removeNode(v3s16 p)
2461 std::map<v3s16, MapBlock*> modified_blocks;
2465 //TimeTaker t("removeNodeAndUpdate", m_device);
2466 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2468 catch(InvalidPositionException &e)
2472 // add urgent task to update the modified node
2473 addUpdateMeshTaskForNode(p, false, true);
2475 for(std::map<v3s16, MapBlock * >::iterator
2476 i = modified_blocks.begin();
2477 i != modified_blocks.end(); ++i)
2479 addUpdateMeshTaskWithEdge(i->first);
2483 void Client::addNode(v3s16 p, MapNode n)
2485 TimeTaker timer1("Client::addNode()");
2487 std::map<v3s16, MapBlock*> modified_blocks;
2491 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2492 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2494 catch(InvalidPositionException &e)
2497 for(std::map<v3s16, MapBlock * >::iterator
2498 i = modified_blocks.begin();
2499 i != modified_blocks.end(); ++i)
2501 addUpdateMeshTaskWithEdge(i->first);
2505 void Client::setPlayerControl(PlayerControl &control)
2507 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2508 LocalPlayer *player = m_env.getLocalPlayer();
2509 assert(player != NULL);
2510 player->control = control;
2513 void Client::selectPlayerItem(u16 item)
2515 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2516 m_playeritem = item;
2517 m_inventory_updated = true;
2518 sendPlayerItem(item);
2521 // Returns true if the inventory of the local player has been
2522 // updated from the server. If it is true, it is set to false.
2523 bool Client::getLocalInventoryUpdated()
2525 // m_inventory_updated is behind envlock
2526 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2527 bool updated = m_inventory_updated;
2528 m_inventory_updated = false;
2532 // Copies the inventory of the local player to parameter
2533 void Client::getLocalInventory(Inventory &dst)
2535 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2536 Player *player = m_env.getLocalPlayer();
2537 assert(player != NULL);
2538 dst = player->inventory;
2541 Inventory* Client::getInventory(const InventoryLocation &loc)
2544 case InventoryLocation::UNDEFINED:
2547 case InventoryLocation::CURRENT_PLAYER:
2549 Player *player = m_env.getLocalPlayer();
2550 assert(player != NULL);
2551 return &player->inventory;
2554 case InventoryLocation::PLAYER:
2556 Player *player = m_env.getPlayer(loc.name.c_str());
2559 return &player->inventory;
2562 case InventoryLocation::NODEMETA:
2564 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2567 return meta->getInventory();
2570 case InventoryLocation::DETACHED:
2572 if(m_detached_inventories.count(loc.name) == 0)
2574 return m_detached_inventories[loc.name];
2582 void Client::inventoryAction(InventoryAction *a)
2585 Send it to the server
2587 sendInventoryAction(a);
2590 Predict some local inventory changes
2592 a->clientApply(this, this);
2598 ClientActiveObject * Client::getSelectedActiveObject(
2600 v3f from_pos_f_on_map,
2601 core::line3d<f32> shootline_on_map
2604 std::vector<DistanceSortedActiveObject> objects;
2606 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2608 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2611 // After this, the closest object is the first in the array.
2612 std::sort(objects.begin(), objects.end());
2614 for(u32 i=0; i<objects.size(); i++)
2616 ClientActiveObject *obj = objects[i].obj;
2618 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2619 if(selection_box == NULL)
2622 v3f pos = obj->getPosition();
2624 core::aabbox3d<f32> offsetted_box(
2625 selection_box->MinEdge + pos,
2626 selection_box->MaxEdge + pos
2629 if(offsetted_box.intersectsWithLine(shootline_on_map))
2631 //infostream<<"Returning selected object"<<std::endl;
2636 //infostream<<"No object selected; returning NULL."<<std::endl;
2640 void Client::printDebugInfo(std::ostream &os)
2642 //JMutexAutoLock lock1(m_fetchblock_mutex);
2643 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2645 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2646 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2647 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2651 std::list<std::string> Client::getConnectedPlayerNames()
2653 return m_env.getPlayerNames();
2656 float Client::getAnimationTime()
2658 return m_animation_time;
2661 int Client::getCrackLevel()
2663 return m_crack_level;
2666 void Client::setCrack(int level, v3s16 pos)
2668 int old_crack_level = m_crack_level;
2669 v3s16 old_crack_pos = m_crack_pos;
2671 m_crack_level = level;
2674 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2677 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2679 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2682 addUpdateMeshTaskForNode(pos, false, true);
2688 Player *player = m_env.getLocalPlayer();
2689 assert(player != NULL);
2693 u16 Client::getBreath()
2695 Player *player = m_env.getLocalPlayer();
2696 assert(player != NULL);
2697 return player->breath;
2700 bool Client::getChatMessage(std::wstring &message)
2702 if(m_chat_queue.size() == 0)
2704 message = m_chat_queue.pop_front();
2708 void Client::typeChatMessage(const std::wstring &message)
2710 // Discard empty line
2715 sendChatMessage(message);
2718 if (message[0] == L'/')
2720 m_chat_queue.push_back(
2721 (std::wstring)L"issued command: "+message);
2725 LocalPlayer *player = m_env.getLocalPlayer();
2726 assert(player != NULL);
2727 std::wstring name = narrow_to_wide(player->getName());
2728 m_chat_queue.push_back(
2729 (std::wstring)L"<"+name+L"> "+message);
2733 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2735 /*infostream<<"Client::addUpdateMeshTask(): "
2736 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2737 <<" ack_to_server="<<ack_to_server
2738 <<" urgent="<<urgent
2741 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2746 Create a task to update the mesh of the block
2749 MeshMakeData *data = new MeshMakeData(this);
2752 //TimeTaker timer("data fill");
2754 // Debug: 1-6ms, avg=2ms
2756 data->setCrack(m_crack_level, m_crack_pos);
2757 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2761 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2763 // Add task to queue
2764 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2766 /*infostream<<"Mesh update input queue size is "
2767 <<m_mesh_update_thread.m_queue_in.size()
2771 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2775 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2776 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2781 v3s16 p = blockpos + v3s16(0,0,0);
2782 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2783 addUpdateMeshTask(p, ack_to_server, urgent);
2785 catch(InvalidPositionException &e){}
2787 for (int i=0;i<6;i++)
2790 v3s16 p = blockpos + g_6dirs[i];
2791 addUpdateMeshTask(p, false, urgent);
2793 catch(InvalidPositionException &e){}
2797 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2801 infostream<<"Client::addUpdateMeshTaskForNode(): "
2802 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2806 v3s16 blockpos = getNodeBlockPos(nodepos);
2807 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2810 v3s16 p = blockpos + v3s16(0,0,0);
2811 addUpdateMeshTask(p, ack_to_server, urgent);
2813 catch(InvalidPositionException &e){}
2815 if(nodepos.X == blockpos_relative.X){
2817 v3s16 p = blockpos + v3s16(-1,0,0);
2818 addUpdateMeshTask(p, false, urgent);
2820 catch(InvalidPositionException &e){}
2822 if(nodepos.Y == blockpos_relative.Y){
2824 v3s16 p = blockpos + v3s16(0,-1,0);
2825 addUpdateMeshTask(p, false, urgent);
2827 catch(InvalidPositionException &e){}
2829 if(nodepos.Z == blockpos_relative.Z){
2831 v3s16 p = blockpos + v3s16(0,0,-1);
2832 addUpdateMeshTask(p, false, urgent);
2834 catch(InvalidPositionException &e){}
2838 ClientEvent Client::getClientEvent()
2840 if(m_client_event_queue.size() == 0)
2843 event.type = CE_NONE;
2846 return m_client_event_queue.pop_front();
2849 void draw_load_screen(const std::wstring &text,
2850 IrrlichtDevice* device, gui::IGUIFont* font,
2851 float dtime=0 ,int percent=0, bool clouds=true);
2852 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2854 infostream<<"Client::afterContentReceived() started"<<std::endl;
2855 assert(m_itemdef_received);
2856 assert(m_nodedef_received);
2857 assert(texturesReceived());
2859 // remove the information about which checksum each texture
2861 m_media_name_sha1_map.clear();
2863 // Rebuild inherited images and recreate textures
2864 infostream<<"- Rebuilding images and textures"<<std::endl;
2865 m_tsrc->rebuildImagesAndTextures();
2868 infostream<<"- Rebuilding shaders"<<std::endl;
2869 m_shsrc->rebuildShaders();
2871 // Update node aliases
2872 infostream<<"- Updating node aliases"<<std::endl;
2873 m_nodedef->updateAliases(m_itemdef);
2875 // Update node textures
2876 infostream<<"- Updating node textures"<<std::endl;
2877 m_nodedef->updateTextures(m_tsrc);
2879 // Preload item textures and meshes if configured to
2880 if(g_settings->getBool("preload_item_visuals"))
2882 verbosestream<<"Updating item textures and meshes"<<std::endl;
2883 wchar_t* text = wgettext("Item textures...");
2884 draw_load_screen(text,device,font,0,0);
2885 std::set<std::string> names = m_itemdef->getAll();
2886 size_t size = names.size();
2889 for(std::set<std::string>::const_iterator
2890 i = names.begin(); i != names.end(); ++i){
2891 // Asking for these caches the result
2892 m_itemdef->getInventoryTexture(*i, this);
2893 m_itemdef->getWieldMesh(*i, this);
2895 percent = count*100/size;
2896 if (count%50 == 0) // only update every 50 item
2897 draw_load_screen(text,device,font,0,percent);
2902 // Start mesh update thread after setting up content definitions
2903 infostream<<"- Starting mesh update thread"<<std::endl;
2904 m_mesh_update_thread.Start();
2906 infostream<<"Client::afterContentReceived() done"<<std::endl;
2909 float Client::getRTT(void)
2912 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2913 } catch(con::PeerNotFoundException &e){
2918 // IGameDef interface
2920 IItemDefManager* Client::getItemDefManager()
2924 INodeDefManager* Client::getNodeDefManager()
2928 ICraftDefManager* Client::getCraftDefManager()
2931 //return m_craftdef;
2933 ITextureSource* Client::getTextureSource()
2937 IShaderSource* Client::getShaderSource()
2941 u16 Client::allocateUnknownNodeId(const std::string &name)
2943 errorstream<<"Client::allocateUnknownNodeId(): "
2944 <<"Client cannot allocate node IDs"<<std::endl;
2946 return CONTENT_IGNORE;
2948 ISoundManager* Client::getSoundManager()
2952 MtEventManager* Client::getEventManager()