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);
1265 bool remove_metadata = true;
1266 u32 index = 8 + MapNode::serializedLength(ser_version);
1267 if ((datasize >= index+1) && data[index]){
1268 remove_metadata = false;
1271 addNode(p, n, remove_metadata);
1273 else if(command == TOCLIENT_BLOCKDATA)
1275 // Ignore too small packet
1280 p.X = readS16(&data[2]);
1281 p.Y = readS16(&data[4]);
1282 p.Z = readS16(&data[6]);
1284 /*infostream<<"Client: Thread: BLOCKDATA for ("
1285 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1286 /*infostream<<"Client: Thread: BLOCKDATA for ("
1287 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1289 std::string datastring((char*)&data[8], datasize-8);
1290 std::istringstream istr(datastring, std::ios_base::binary);
1295 v2s16 p2d(p.X, p.Z);
1296 sector = m_env.getMap().emergeSector(p2d);
1298 assert(sector->getPos() == p2d);
1300 //TimeTaker timer("MapBlock deSerialize");
1303 block = sector->getBlockNoCreateNoEx(p.Y);
1307 Update an existing block
1309 //infostream<<"Updating"<<std::endl;
1310 block->deSerialize(istr, ser_version, false);
1311 block->deSerializeNetworkSpecific(istr);
1318 //infostream<<"Creating new"<<std::endl;
1319 block = new MapBlock(&m_env.getMap(), p, this);
1320 block->deSerialize(istr, ser_version, false);
1321 block->deSerializeNetworkSpecific(istr);
1322 sector->insertBlock(block);
1336 u32 replysize = 2+1+6;
1337 SharedBuffer<u8> reply(replysize);
1338 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1340 writeV3S16(&reply[3], p);
1342 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1346 Add it to mesh update queue and set it to be acknowledged after update.
1348 //infostream<<"Adding mesh update task for received block"<<std::endl;
1349 addUpdateMeshTaskWithEdge(p, true);
1351 else if(command == TOCLIENT_INVENTORY)
1356 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1359 //TimeTaker t2("mutex locking", m_device);
1360 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1363 //TimeTaker t3("istringstream init", m_device);
1364 std::string datastring((char*)&data[2], datasize-2);
1365 std::istringstream is(datastring, std::ios_base::binary);
1368 //TimeTaker t4("player get", m_device);
1369 Player *player = m_env.getLocalPlayer();
1370 assert(player != NULL);
1373 //TimeTaker t1("inventory.deSerialize()", m_device);
1374 player->inventory.deSerialize(is);
1377 m_inventory_updated = true;
1379 delete m_inventory_from_server;
1380 m_inventory_from_server = new Inventory(player->inventory);
1381 m_inventory_from_server_age = 0.0;
1383 //infostream<<"Client got player inventory:"<<std::endl;
1384 //player->inventory.print(infostream);
1387 else if(command == TOCLIENT_TIME_OF_DAY)
1392 u16 time_of_day = readU16(&data[2]);
1393 time_of_day = time_of_day % 24000;
1394 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1395 float time_speed = 0;
1396 if(datasize >= 2 + 2 + 4){
1397 time_speed = readF1000(&data[4]);
1399 // Old message; try to approximate speed of time by ourselves
1400 float time_of_day_f = (float)time_of_day / 24000.0;
1401 float tod_diff_f = 0;
1402 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1403 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1405 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1406 m_last_time_of_day_f = time_of_day_f;
1407 float time_diff = m_time_of_day_update_timer;
1408 m_time_of_day_update_timer = 0;
1409 if(m_time_of_day_set){
1410 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1411 infostream<<"Client: Measured time_of_day speed (old format): "
1412 <<time_speed<<" tod_diff_f="<<tod_diff_f
1413 <<" time_diff="<<time_diff<<std::endl;
1417 // Update environment
1418 m_env.setTimeOfDay(time_of_day);
1419 m_env.setTimeOfDaySpeed(time_speed);
1420 m_time_of_day_set = true;
1422 u32 dr = m_env.getDayNightRatio();
1423 verbosestream<<"Client: time_of_day="<<time_of_day
1424 <<" time_speed="<<time_speed
1425 <<" dr="<<dr<<std::endl;
1427 else if(command == TOCLIENT_CHAT_MESSAGE)
1435 std::string datastring((char*)&data[2], datasize-2);
1436 std::istringstream is(datastring, std::ios_base::binary);
1439 is.read((char*)buf, 2);
1440 u16 len = readU16(buf);
1442 std::wstring message;
1443 for(u16 i=0; i<len; i++)
1445 is.read((char*)buf, 2);
1446 message += (wchar_t)readU16(buf);
1449 /*infostream<<"Client received chat message: "
1450 <<wide_to_narrow(message)<<std::endl;*/
1452 m_chat_queue.push_back(message);
1454 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1456 //if(g_settings->getBool("enable_experimental"))
1460 u16 count of removed objects
1461 for all removed objects {
1464 u16 count of added objects
1465 for all added objects {
1468 u32 initialization data length
1469 string initialization data
1474 // Get all data except the command number
1475 std::string datastring((char*)&data[2], datasize-2);
1476 // Throw them in an istringstream
1477 std::istringstream is(datastring, std::ios_base::binary);
1481 // Read removed objects
1483 u16 removed_count = readU16((u8*)buf);
1484 for(u16 i=0; i<removed_count; i++)
1487 u16 id = readU16((u8*)buf);
1490 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1491 m_env.removeActiveObject(id);
1495 // Read added objects
1497 u16 added_count = readU16((u8*)buf);
1498 for(u16 i=0; i<added_count; i++)
1501 u16 id = readU16((u8*)buf);
1503 u8 type = readU8((u8*)buf);
1504 std::string data = deSerializeLongString(is);
1507 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1508 m_env.addActiveObject(id, type, data);
1513 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1515 //if(g_settings->getBool("enable_experimental"))
1527 // Get all data except the command number
1528 std::string datastring((char*)&data[2], datasize-2);
1529 // Throw them in an istringstream
1530 std::istringstream is(datastring, std::ios_base::binary);
1532 while(is.eof() == false)
1536 u16 id = readU16((u8*)buf);
1540 u16 message_size = readU16((u8*)buf);
1541 std::string message;
1542 message.reserve(message_size);
1543 for(u16 i=0; i<message_size; i++)
1546 message.append(buf, 1);
1548 // Pass on to the environment
1550 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1551 m_env.processActiveObjectMessage(id, message);
1556 else if(command == TOCLIENT_MOVEMENT)
1558 std::string datastring((char*)&data[2], datasize-2);
1559 std::istringstream is(datastring, std::ios_base::binary);
1560 Player *player = m_env.getLocalPlayer();
1561 assert(player != NULL);
1563 player->movement_acceleration_default = readF1000(is) * BS;
1564 player->movement_acceleration_air = readF1000(is) * BS;
1565 player->movement_acceleration_fast = readF1000(is) * BS;
1566 player->movement_speed_walk = readF1000(is) * BS;
1567 player->movement_speed_crouch = readF1000(is) * BS;
1568 player->movement_speed_fast = readF1000(is) * BS;
1569 player->movement_speed_climb = readF1000(is) * BS;
1570 player->movement_speed_jump = readF1000(is) * BS;
1571 player->movement_liquid_fluidity = readF1000(is) * BS;
1572 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1573 player->movement_liquid_sink = readF1000(is) * BS;
1574 player->movement_gravity = readF1000(is) * BS;
1576 else if(command == TOCLIENT_HP)
1578 std::string datastring((char*)&data[2], datasize-2);
1579 std::istringstream is(datastring, std::ios_base::binary);
1580 Player *player = m_env.getLocalPlayer();
1581 assert(player != NULL);
1582 u8 oldhp = player->hp;
1588 // Add to ClientEvent queue
1590 event.type = CE_PLAYER_DAMAGE;
1591 event.player_damage.amount = oldhp - hp;
1592 m_client_event_queue.push_back(event);
1595 else if(command == TOCLIENT_BREATH)
1597 std::string datastring((char*)&data[2], datasize-2);
1598 std::istringstream is(datastring, std::ios_base::binary);
1599 Player *player = m_env.getLocalPlayer();
1600 assert(player != NULL);
1601 u16 breath = readU16(is);
1602 player->setBreath(breath) ;
1604 else if(command == TOCLIENT_MOVE_PLAYER)
1606 std::string datastring((char*)&data[2], datasize-2);
1607 std::istringstream is(datastring, std::ios_base::binary);
1608 Player *player = m_env.getLocalPlayer();
1609 assert(player != NULL);
1610 v3f pos = readV3F1000(is);
1611 f32 pitch = readF1000(is);
1612 f32 yaw = readF1000(is);
1613 player->setPosition(pos);
1614 /*player->setPitch(pitch);
1615 player->setYaw(yaw);*/
1617 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1618 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1624 Add to ClientEvent queue.
1625 This has to be sent to the main program because otherwise
1626 it would just force the pitch and yaw values to whatever
1627 the camera points to.
1630 event.type = CE_PLAYER_FORCE_MOVE;
1631 event.player_force_move.pitch = pitch;
1632 event.player_force_move.yaw = yaw;
1633 m_client_event_queue.push_back(event);
1635 // Ignore damage for a few seconds, so that the player doesn't
1636 // get damage from falling on ground
1637 m_ignore_damage_timer = 3.0;
1639 else if(command == TOCLIENT_PLAYERITEM)
1641 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1643 else if(command == TOCLIENT_DEATHSCREEN)
1645 std::string datastring((char*)&data[2], datasize-2);
1646 std::istringstream is(datastring, std::ios_base::binary);
1648 bool set_camera_point_target = readU8(is);
1649 v3f camera_point_target = readV3F1000(is);
1652 event.type = CE_DEATHSCREEN;
1653 event.deathscreen.set_camera_point_target = set_camera_point_target;
1654 event.deathscreen.camera_point_target_x = camera_point_target.X;
1655 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1656 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1657 m_client_event_queue.push_back(event);
1659 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1661 std::string datastring((char*)&data[2], datasize-2);
1662 std::istringstream is(datastring, std::ios_base::binary);
1664 // Mesh update thread must be stopped while
1665 // updating content definitions
1666 assert(!m_mesh_update_thread.IsRunning());
1668 int num_files = readU16(is);
1670 infostream<<"Client: Received media announcement: packet size: "
1671 <<datasize<<std::endl;
1673 std::list<MediaRequest> file_requests;
1675 for(int i=0; i<num_files; i++)
1677 //read file from cache
1678 std::string name = deSerializeString(is);
1679 std::string sha1_base64 = deSerializeString(is);
1681 // if name contains illegal characters, ignore the file
1682 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1683 errorstream<<"Client: ignoring illegal file name "
1684 <<"sent by server: \""<<name<<"\""<<std::endl;
1688 std::string sha1_raw = base64_decode(sha1_base64);
1689 std::string sha1_hex = hex_encode(sha1_raw);
1690 std::ostringstream tmp_os(std::ios_base::binary);
1691 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1692 m_media_name_sha1_map[name] = sha1_raw;
1694 // If found in cache, try to load it from there
1697 bool success = loadMedia(tmp_os.str(), name);
1699 verbosestream<<"Client: Loaded cached media: "
1700 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1703 infostream<<"Client: Failed to load cached media: "
1704 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1707 // Didn't load from cache; queue it to be requested
1708 verbosestream<<"Client: Adding file to request list: \""
1709 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1710 file_requests.push_back(MediaRequest(name));
1713 std::string remote_media = "";
1715 remote_media = deSerializeString(is);
1717 catch(SerializationError) {
1718 // not supported by server or turned off
1721 m_media_count = file_requests.size();
1722 m_media_receive_started = true;
1724 if (remote_media == "" || !USE_CURL) {
1725 request_media(file_requests);
1728 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1729 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1730 i != file_requests.end(); ++i) {
1731 (*cur)->m_file_requests.push_back(*i);
1733 if (cur == m_media_fetch_threads.end())
1734 cur = m_media_fetch_threads.begin();
1736 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1737 i != m_media_fetch_threads.end(); ++i) {
1738 (*i)->m_remote_url = remote_media;
1743 // notify server we received everything
1744 std::ostringstream os(std::ios_base::binary);
1745 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1746 std::string s = os.str();
1747 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1749 Send(0, data, true);
1752 event.type = CE_TEXTURES_UPDATED;
1753 m_client_event_queue.push_back(event);
1755 else if(command == TOCLIENT_MEDIA)
1757 std::string datastring((char*)&data[2], datasize-2);
1758 std::istringstream is(datastring, std::ios_base::binary);
1762 u16 total number of file bunches
1763 u16 index of this bunch
1764 u32 number of files in this bunch
1772 int num_bunches = readU16(is);
1773 int bunch_i = readU16(is);
1774 u32 num_files = readU32(is);
1775 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1776 <<num_bunches<<" files="<<num_files
1777 <<" size="<<datasize<<std::endl;
1779 // Check total and received media count
1780 assert(m_media_received_count <= m_media_count);
1781 if (num_files > m_media_count - m_media_received_count) {
1782 errorstream<<"Client: Received more files than requested:"
1783 <<" total count="<<m_media_count
1784 <<" total received="<<m_media_received_count
1785 <<" bunch "<<bunch_i<<"/"<<num_bunches
1786 <<" files="<<num_files
1787 <<" size="<<datasize<<std::endl;
1788 num_files = m_media_count - m_media_received_count;
1793 // Mesh update thread must be stopped while
1794 // updating content definitions
1795 assert(!m_mesh_update_thread.IsRunning());
1797 for(u32 i=0; i<num_files; i++){
1798 assert(m_media_received_count < m_media_count);
1799 m_media_received_count++;
1800 std::string name = deSerializeString(is);
1801 std::string data = deSerializeLongString(is);
1803 // if name contains illegal characters, ignore the file
1804 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1805 errorstream<<"Client: ignoring illegal file name "
1806 <<"sent by server: \""<<name<<"\""<<std::endl;
1810 bool success = loadMedia(data, name);
1812 verbosestream<<"Client: Loaded received media: "
1813 <<"\""<<name<<"\". Caching."<<std::endl;
1815 infostream<<"Client: Failed to load received media: "
1816 <<"\""<<name<<"\". Not caching."<<std::endl;
1820 bool did = fs::CreateAllDirs(getMediaCacheDir());
1822 errorstream<<"Could not create media cache directory"
1827 std::map<std::string, std::string>::iterator n;
1828 n = m_media_name_sha1_map.find(name);
1829 if(n == m_media_name_sha1_map.end())
1830 errorstream<<"The server sent a file that has not "
1831 <<"been announced."<<std::endl;
1833 m_media_cache.update_sha1(data);
1838 event.type = CE_TEXTURES_UPDATED;
1839 m_client_event_queue.push_back(event);
1841 else if(command == TOCLIENT_TOOLDEF)
1843 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1845 else if(command == TOCLIENT_NODEDEF)
1847 infostream<<"Client: Received node definitions: packet size: "
1848 <<datasize<<std::endl;
1850 // Mesh update thread must be stopped while
1851 // updating content definitions
1852 assert(!m_mesh_update_thread.IsRunning());
1854 // Decompress node definitions
1855 std::string datastring((char*)&data[2], datasize-2);
1856 std::istringstream is(datastring, std::ios_base::binary);
1857 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1858 std::ostringstream tmp_os;
1859 decompressZlib(tmp_is, tmp_os);
1861 // Deserialize node definitions
1862 std::istringstream tmp_is2(tmp_os.str());
1863 m_nodedef->deSerialize(tmp_is2);
1864 m_nodedef_received = true;
1866 else if(command == TOCLIENT_CRAFTITEMDEF)
1868 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1870 else if(command == TOCLIENT_ITEMDEF)
1872 infostream<<"Client: Received item definitions: packet size: "
1873 <<datasize<<std::endl;
1875 // Mesh update thread must be stopped while
1876 // updating content definitions
1877 assert(!m_mesh_update_thread.IsRunning());
1879 // Decompress item definitions
1880 std::string datastring((char*)&data[2], datasize-2);
1881 std::istringstream is(datastring, std::ios_base::binary);
1882 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1883 std::ostringstream tmp_os;
1884 decompressZlib(tmp_is, tmp_os);
1886 // Deserialize node definitions
1887 std::istringstream tmp_is2(tmp_os.str());
1888 m_itemdef->deSerialize(tmp_is2);
1889 m_itemdef_received = true;
1891 else if(command == TOCLIENT_PLAY_SOUND)
1893 std::string datastring((char*)&data[2], datasize-2);
1894 std::istringstream is(datastring, std::ios_base::binary);
1896 s32 server_id = readS32(is);
1897 std::string name = deSerializeString(is);
1898 float gain = readF1000(is);
1899 int type = readU8(is); // 0=local, 1=positional, 2=object
1900 v3f pos = readV3F1000(is);
1901 u16 object_id = readU16(is);
1902 bool loop = readU8(is);
1907 client_id = m_sound->playSound(name, loop, gain);
1909 case 1: // positional
1910 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1913 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1915 pos = cao->getPosition();
1916 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1917 // TODO: Set up sound to move with object
1922 if(client_id != -1){
1923 m_sounds_server_to_client[server_id] = client_id;
1924 m_sounds_client_to_server[client_id] = server_id;
1926 m_sounds_to_objects[client_id] = object_id;
1929 else if(command == TOCLIENT_STOP_SOUND)
1931 std::string datastring((char*)&data[2], datasize-2);
1932 std::istringstream is(datastring, std::ios_base::binary);
1934 s32 server_id = readS32(is);
1935 std::map<s32, int>::iterator i =
1936 m_sounds_server_to_client.find(server_id);
1937 if(i != m_sounds_server_to_client.end()){
1938 int client_id = i->second;
1939 m_sound->stopSound(client_id);
1942 else if(command == TOCLIENT_PRIVILEGES)
1944 std::string datastring((char*)&data[2], datasize-2);
1945 std::istringstream is(datastring, std::ios_base::binary);
1947 m_privileges.clear();
1948 infostream<<"Client: Privileges updated: ";
1949 u16 num_privileges = readU16(is);
1950 for(u16 i=0; i<num_privileges; i++){
1951 std::string priv = deSerializeString(is);
1952 m_privileges.insert(priv);
1953 infostream<<priv<<" ";
1955 infostream<<std::endl;
1957 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1959 std::string datastring((char*)&data[2], datasize-2);
1960 std::istringstream is(datastring, std::ios_base::binary);
1962 // Store formspec in LocalPlayer
1963 Player *player = m_env.getLocalPlayer();
1964 assert(player != NULL);
1965 player->inventory_formspec = deSerializeLongString(is);
1967 else if(command == TOCLIENT_DETACHED_INVENTORY)
1969 std::string datastring((char*)&data[2], datasize-2);
1970 std::istringstream is(datastring, std::ios_base::binary);
1972 std::string name = deSerializeString(is);
1974 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1976 Inventory *inv = NULL;
1977 if(m_detached_inventories.count(name) > 0)
1978 inv = m_detached_inventories[name];
1980 inv = new Inventory(m_itemdef);
1981 m_detached_inventories[name] = inv;
1983 inv->deSerialize(is);
1985 else if(command == TOCLIENT_SHOW_FORMSPEC)
1987 std::string datastring((char*)&data[2], datasize-2);
1988 std::istringstream is(datastring, std::ios_base::binary);
1990 std::string formspec = deSerializeLongString(is);
1991 std::string formname = deSerializeString(is);
1994 event.type = CE_SHOW_FORMSPEC;
1995 // pointer is required as event is a struct only!
1996 // adding a std:string to a struct isn't possible
1997 event.show_formspec.formspec = new std::string(formspec);
1998 event.show_formspec.formname = new std::string(formname);
1999 m_client_event_queue.push_back(event);
2001 else if(command == TOCLIENT_SPAWN_PARTICLE)
2003 std::string datastring((char*)&data[2], datasize-2);
2004 std::istringstream is(datastring, std::ios_base::binary);
2006 v3f pos = readV3F1000(is);
2007 v3f vel = readV3F1000(is);
2008 v3f acc = readV3F1000(is);
2009 float expirationtime = readF1000(is);
2010 float size = readF1000(is);
2011 bool collisiondetection = readU8(is);
2012 std::string texture = deSerializeLongString(is);
2015 event.type = CE_SPAWN_PARTICLE;
2016 event.spawn_particle.pos = new v3f (pos);
2017 event.spawn_particle.vel = new v3f (vel);
2018 event.spawn_particle.acc = new v3f (acc);
2020 event.spawn_particle.expirationtime = expirationtime;
2021 event.spawn_particle.size = size;
2022 event.spawn_particle.collisiondetection =
2024 event.spawn_particle.texture = new std::string(texture);
2026 m_client_event_queue.push_back(event);
2028 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
2030 std::string datastring((char*)&data[2], datasize-2);
2031 std::istringstream is(datastring, std::ios_base::binary);
2033 u16 amount = readU16(is);
2034 float spawntime = readF1000(is);
2035 v3f minpos = readV3F1000(is);
2036 v3f maxpos = readV3F1000(is);
2037 v3f minvel = readV3F1000(is);
2038 v3f maxvel = readV3F1000(is);
2039 v3f minacc = readV3F1000(is);
2040 v3f maxacc = readV3F1000(is);
2041 float minexptime = readF1000(is);
2042 float maxexptime = readF1000(is);
2043 float minsize = readF1000(is);
2044 float maxsize = readF1000(is);
2045 bool collisiondetection = readU8(is);
2046 std::string texture = deSerializeLongString(is);
2047 u32 id = readU32(is);
2050 event.type = CE_ADD_PARTICLESPAWNER;
2051 event.add_particlespawner.amount = amount;
2052 event.add_particlespawner.spawntime = spawntime;
2054 event.add_particlespawner.minpos = new v3f (minpos);
2055 event.add_particlespawner.maxpos = new v3f (maxpos);
2056 event.add_particlespawner.minvel = new v3f (minvel);
2057 event.add_particlespawner.maxvel = new v3f (maxvel);
2058 event.add_particlespawner.minacc = new v3f (minacc);
2059 event.add_particlespawner.maxacc = new v3f (maxacc);
2061 event.add_particlespawner.minexptime = minexptime;
2062 event.add_particlespawner.maxexptime = maxexptime;
2063 event.add_particlespawner.minsize = minsize;
2064 event.add_particlespawner.maxsize = maxsize;
2065 event.add_particlespawner.collisiondetection = collisiondetection;
2066 event.add_particlespawner.texture = new std::string(texture);
2067 event.add_particlespawner.id = id;
2069 m_client_event_queue.push_back(event);
2071 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2073 std::string datastring((char*)&data[2], datasize-2);
2074 std::istringstream is(datastring, std::ios_base::binary);
2076 u32 id = readU16(is);
2079 event.type = CE_DELETE_PARTICLESPAWNER;
2080 event.delete_particlespawner.id = id;
2082 m_client_event_queue.push_back(event);
2084 else if(command == TOCLIENT_HUDADD)
2086 std::string datastring((char *)&data[2], datasize - 2);
2087 std::istringstream is(datastring, std::ios_base::binary);
2089 u32 id = readU32(is);
2090 u8 type = readU8(is);
2091 v2f pos = readV2F1000(is);
2092 std::string name = deSerializeString(is);
2093 v2f scale = readV2F1000(is);
2094 std::string text = deSerializeString(is);
2095 u32 number = readU32(is);
2096 u32 item = readU32(is);
2097 u32 dir = readU32(is);
2098 v2f align = readV2F1000(is);
2099 v2f offset = readV2F1000(is);
2102 event.type = CE_HUDADD;
2103 event.hudadd.id = id;
2104 event.hudadd.type = type;
2105 event.hudadd.pos = new v2f(pos);
2106 event.hudadd.name = new std::string(name);
2107 event.hudadd.scale = new v2f(scale);
2108 event.hudadd.text = new std::string(text);
2109 event.hudadd.number = number;
2110 event.hudadd.item = item;
2111 event.hudadd.dir = dir;
2112 event.hudadd.align = new v2f(align);
2113 event.hudadd.offset = new v2f(offset);
2114 m_client_event_queue.push_back(event);
2116 else if(command == TOCLIENT_HUDRM)
2118 std::string datastring((char *)&data[2], datasize - 2);
2119 std::istringstream is(datastring, std::ios_base::binary);
2121 u32 id = readU32(is);
2124 event.type = CE_HUDRM;
2125 event.hudrm.id = id;
2126 m_client_event_queue.push_back(event);
2128 else if(command == TOCLIENT_HUDCHANGE)
2134 std::string datastring((char *)&data[2], datasize - 2);
2135 std::istringstream is(datastring, std::ios_base::binary);
2137 u32 id = readU32(is);
2138 u8 stat = (HudElementStat)readU8(is);
2140 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2141 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2142 v2fdata = readV2F1000(is);
2143 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2144 sdata = deSerializeString(is);
2146 intdata = readU32(is);
2149 event.type = CE_HUDCHANGE;
2150 event.hudchange.id = id;
2151 event.hudchange.stat = (HudElementStat)stat;
2152 event.hudchange.v2fdata = new v2f(v2fdata);
2153 event.hudchange.sdata = new std::string(sdata);
2154 event.hudchange.data = intdata;
2155 m_client_event_queue.push_back(event);
2157 else if(command == TOCLIENT_HUD_SET_FLAGS)
2159 std::string datastring((char *)&data[2], datasize - 2);
2160 std::istringstream is(datastring, std::ios_base::binary);
2162 Player *player = m_env.getLocalPlayer();
2163 assert(player != NULL);
2165 u32 flags = readU32(is);
2166 u32 mask = readU32(is);
2168 player->hud_flags &= ~mask;
2169 player->hud_flags |= flags;
2171 else if(command == TOCLIENT_HUD_SET_PARAM)
2173 std::string datastring((char *)&data[2], datasize - 2);
2174 std::istringstream is(datastring, std::ios_base::binary);
2176 Player *player = m_env.getLocalPlayer();
2177 assert(player != NULL);
2179 u16 param = readU16(is);
2180 std::string value = deSerializeString(is);
2182 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2183 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2184 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2185 player->hud_hotbar_itemcount = hotbar_itemcount;
2186 } else if (param == HUD_PARAM_HOTBAR_IMAGE) {
2187 ((LocalPlayer *) player)->hotbar_image = value;
2188 } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
2189 ((LocalPlayer *) player)->hotbar_selected_image = value;
2194 infostream<<"Client: Ignoring unknown command "
2195 <<command<<std::endl;
2199 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2201 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2202 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2205 void Client::interact(u8 action, const PointedThing& pointed)
2207 if(connectedAndInitialized() == false){
2208 infostream<<"Client::interact() "
2209 "cancelled (not connected)"
2214 std::ostringstream os(std::ios_base::binary);
2220 [5] u32 length of the next item
2221 [9] serialized PointedThing
2223 0: start digging (from undersurface) or use
2224 1: stop digging (all parameters ignored)
2225 2: digging completed
2226 3: place block or item (to abovesurface)
2229 writeU16(os, TOSERVER_INTERACT);
2230 writeU8(os, action);
2231 writeU16(os, getPlayerItem());
2232 std::ostringstream tmp_os(std::ios::binary);
2233 pointed.serialize(tmp_os);
2234 os<<serializeLongString(tmp_os.str());
2236 std::string s = os.str();
2237 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2240 Send(0, data, true);
2243 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2244 const std::map<std::string, std::string> &fields)
2246 std::ostringstream os(std::ios_base::binary);
2248 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2250 os<<serializeString(formname);
2251 writeU16(os, fields.size());
2252 for(std::map<std::string, std::string>::const_iterator
2253 i = fields.begin(); i != fields.end(); i++){
2254 const std::string &name = i->first;
2255 const std::string &value = i->second;
2256 os<<serializeString(name);
2257 os<<serializeLongString(value);
2261 std::string s = os.str();
2262 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2264 Send(0, data, true);
2267 void Client::sendInventoryFields(const std::string &formname,
2268 const std::map<std::string, std::string> &fields)
2270 std::ostringstream os(std::ios_base::binary);
2272 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2273 os<<serializeString(formname);
2274 writeU16(os, fields.size());
2275 for(std::map<std::string, std::string>::const_iterator
2276 i = fields.begin(); i != fields.end(); i++){
2277 const std::string &name = i->first;
2278 const std::string &value = i->second;
2279 os<<serializeString(name);
2280 os<<serializeLongString(value);
2284 std::string s = os.str();
2285 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2287 Send(0, data, true);
2290 void Client::sendInventoryAction(InventoryAction *a)
2292 std::ostringstream os(std::ios_base::binary);
2296 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2297 os.write((char*)buf, 2);
2302 std::string s = os.str();
2303 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2305 Send(0, data, true);
2308 void Client::sendChatMessage(const std::wstring &message)
2310 std::ostringstream os(std::ios_base::binary);
2314 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2315 os.write((char*)buf, 2);
2318 writeU16(buf, message.size());
2319 os.write((char*)buf, 2);
2322 for(u32 i=0; i<message.size(); i++)
2326 os.write((char*)buf, 2);
2330 std::string s = os.str();
2331 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2333 Send(0, data, true);
2336 void Client::sendChangePassword(const std::wstring oldpassword,
2337 const std::wstring newpassword)
2339 Player *player = m_env.getLocalPlayer();
2343 std::string playername = player->getName();
2344 std::string oldpwd = translatePassword(playername, oldpassword);
2345 std::string newpwd = translatePassword(playername, newpassword);
2347 std::ostringstream os(std::ios_base::binary);
2348 u8 buf[2+PASSWORD_SIZE*2];
2350 [0] u16 TOSERVER_PASSWORD
2351 [2] u8[28] old password
2352 [30] u8[28] new password
2355 writeU16(buf, TOSERVER_PASSWORD);
2356 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2358 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2359 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2361 buf[2+PASSWORD_SIZE-1] = 0;
2362 buf[30+PASSWORD_SIZE-1] = 0;
2363 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2366 std::string s = os.str();
2367 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2369 Send(0, data, true);
2373 void Client::sendDamage(u8 damage)
2375 DSTACK(__FUNCTION_NAME);
2376 std::ostringstream os(std::ios_base::binary);
2378 writeU16(os, TOSERVER_DAMAGE);
2379 writeU8(os, damage);
2382 std::string s = os.str();
2383 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2385 Send(0, data, true);
2388 void Client::sendBreath(u16 breath)
2390 DSTACK(__FUNCTION_NAME);
2391 std::ostringstream os(std::ios_base::binary);
2393 writeU16(os, TOSERVER_BREATH);
2394 writeU16(os, breath);
2396 std::string s = os.str();
2397 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2399 Send(0, data, true);
2402 void Client::sendRespawn()
2404 DSTACK(__FUNCTION_NAME);
2405 std::ostringstream os(std::ios_base::binary);
2407 writeU16(os, TOSERVER_RESPAWN);
2410 std::string s = os.str();
2411 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2413 Send(0, data, true);
2416 void Client::sendPlayerPos()
2418 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2420 LocalPlayer *myplayer = m_env.getLocalPlayer();
2421 if(myplayer == NULL)
2424 // Save bandwidth by only updating position when something changed
2425 if(myplayer->last_position == myplayer->getPosition() &&
2426 myplayer->last_speed == myplayer->getSpeed() &&
2427 myplayer->last_pitch == myplayer->getPitch() &&
2428 myplayer->last_yaw == myplayer->getYaw() &&
2429 myplayer->last_keyPressed == myplayer->keyPressed)
2432 myplayer->last_position = myplayer->getPosition();
2433 myplayer->last_speed = myplayer->getSpeed();
2434 myplayer->last_pitch = myplayer->getPitch();
2435 myplayer->last_yaw = myplayer->getYaw();
2436 myplayer->last_keyPressed = myplayer->keyPressed;
2440 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2441 our_peer_id = m_con.GetPeerID();
2444 // Set peer id if not set already
2445 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2446 myplayer->peer_id = our_peer_id;
2447 // Check that an existing peer_id is the same as the connection's
2448 assert(myplayer->peer_id == our_peer_id);
2450 v3f pf = myplayer->getPosition();
2451 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2452 v3f sf = myplayer->getSpeed();
2453 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2454 s32 pitch = myplayer->getPitch() * 100;
2455 s32 yaw = myplayer->getYaw() * 100;
2456 u32 keyPressed=myplayer->keyPressed;
2460 [2] v3s32 position*100
2461 [2+12] v3s32 speed*100
2462 [2+12+12] s32 pitch*100
2463 [2+12+12+4] s32 yaw*100
2464 [2+12+12+4+4] u32 keyPressed
2466 SharedBuffer<u8> data(2+12+12+4+4+4);
2467 writeU16(&data[0], TOSERVER_PLAYERPOS);
2468 writeV3S32(&data[2], position);
2469 writeV3S32(&data[2+12], speed);
2470 writeS32(&data[2+12+12], pitch);
2471 writeS32(&data[2+12+12+4], yaw);
2472 writeU32(&data[2+12+12+4+4], keyPressed);
2473 // Send as unreliable
2474 Send(0, data, false);
2477 void Client::sendPlayerItem(u16 item)
2479 Player *myplayer = m_env.getLocalPlayer();
2480 if(myplayer == NULL)
2483 u16 our_peer_id = m_con.GetPeerID();
2485 // Set peer id if not set already
2486 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2487 myplayer->peer_id = our_peer_id;
2488 // Check that an existing peer_id is the same as the connection's
2489 assert(myplayer->peer_id == our_peer_id);
2491 SharedBuffer<u8> data(2+2);
2492 writeU16(&data[0], TOSERVER_PLAYERITEM);
2493 writeU16(&data[2], item);
2496 Send(0, data, true);
2499 void Client::removeNode(v3s16 p)
2501 std::map<v3s16, MapBlock*> modified_blocks;
2505 //TimeTaker t("removeNodeAndUpdate", m_device);
2506 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2508 catch(InvalidPositionException &e)
2512 // add urgent task to update the modified node
2513 addUpdateMeshTaskForNode(p, false, true);
2515 for(std::map<v3s16, MapBlock * >::iterator
2516 i = modified_blocks.begin();
2517 i != modified_blocks.end(); ++i)
2519 addUpdateMeshTaskWithEdge(i->first);
2523 void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
2525 TimeTaker timer1("Client::addNode()");
2527 std::map<v3s16, MapBlock*> modified_blocks;
2531 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2532 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
2534 catch(InvalidPositionException &e)
2537 for(std::map<v3s16, MapBlock * >::iterator
2538 i = modified_blocks.begin();
2539 i != modified_blocks.end(); ++i)
2541 addUpdateMeshTaskWithEdge(i->first);
2545 void Client::setPlayerControl(PlayerControl &control)
2547 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2548 LocalPlayer *player = m_env.getLocalPlayer();
2549 assert(player != NULL);
2550 player->control = control;
2553 void Client::selectPlayerItem(u16 item)
2555 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2556 m_playeritem = item;
2557 m_inventory_updated = true;
2558 sendPlayerItem(item);
2561 // Returns true if the inventory of the local player has been
2562 // updated from the server. If it is true, it is set to false.
2563 bool Client::getLocalInventoryUpdated()
2565 // m_inventory_updated is behind envlock
2566 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2567 bool updated = m_inventory_updated;
2568 m_inventory_updated = false;
2572 // Copies the inventory of the local player to parameter
2573 void Client::getLocalInventory(Inventory &dst)
2575 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2576 Player *player = m_env.getLocalPlayer();
2577 assert(player != NULL);
2578 dst = player->inventory;
2581 Inventory* Client::getInventory(const InventoryLocation &loc)
2584 case InventoryLocation::UNDEFINED:
2587 case InventoryLocation::CURRENT_PLAYER:
2589 Player *player = m_env.getLocalPlayer();
2590 assert(player != NULL);
2591 return &player->inventory;
2594 case InventoryLocation::PLAYER:
2596 Player *player = m_env.getPlayer(loc.name.c_str());
2599 return &player->inventory;
2602 case InventoryLocation::NODEMETA:
2604 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2607 return meta->getInventory();
2610 case InventoryLocation::DETACHED:
2612 if(m_detached_inventories.count(loc.name) == 0)
2614 return m_detached_inventories[loc.name];
2622 void Client::inventoryAction(InventoryAction *a)
2625 Send it to the server
2627 sendInventoryAction(a);
2630 Predict some local inventory changes
2632 a->clientApply(this, this);
2638 ClientActiveObject * Client::getSelectedActiveObject(
2640 v3f from_pos_f_on_map,
2641 core::line3d<f32> shootline_on_map
2644 std::vector<DistanceSortedActiveObject> objects;
2646 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2648 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2651 // After this, the closest object is the first in the array.
2652 std::sort(objects.begin(), objects.end());
2654 for(u32 i=0; i<objects.size(); i++)
2656 ClientActiveObject *obj = objects[i].obj;
2658 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2659 if(selection_box == NULL)
2662 v3f pos = obj->getPosition();
2664 core::aabbox3d<f32> offsetted_box(
2665 selection_box->MinEdge + pos,
2666 selection_box->MaxEdge + pos
2669 if(offsetted_box.intersectsWithLine(shootline_on_map))
2671 //infostream<<"Returning selected object"<<std::endl;
2676 //infostream<<"No object selected; returning NULL."<<std::endl;
2680 void Client::printDebugInfo(std::ostream &os)
2682 //JMutexAutoLock lock1(m_fetchblock_mutex);
2683 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2685 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2686 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2687 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2691 std::list<std::string> Client::getConnectedPlayerNames()
2693 return m_env.getPlayerNames();
2696 float Client::getAnimationTime()
2698 return m_animation_time;
2701 int Client::getCrackLevel()
2703 return m_crack_level;
2706 void Client::setCrack(int level, v3s16 pos)
2708 int old_crack_level = m_crack_level;
2709 v3s16 old_crack_pos = m_crack_pos;
2711 m_crack_level = level;
2714 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2717 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2719 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2722 addUpdateMeshTaskForNode(pos, false, true);
2728 Player *player = m_env.getLocalPlayer();
2729 assert(player != NULL);
2733 u16 Client::getBreath()
2735 Player *player = m_env.getLocalPlayer();
2736 assert(player != NULL);
2737 return player->getBreath();
2740 bool Client::getChatMessage(std::wstring &message)
2742 if(m_chat_queue.size() == 0)
2744 message = m_chat_queue.pop_front();
2748 void Client::typeChatMessage(const std::wstring &message)
2750 // Discard empty line
2755 sendChatMessage(message);
2758 if (message[0] == L'/')
2760 m_chat_queue.push_back(
2761 (std::wstring)L"issued command: "+message);
2765 LocalPlayer *player = m_env.getLocalPlayer();
2766 assert(player != NULL);
2767 std::wstring name = narrow_to_wide(player->getName());
2768 m_chat_queue.push_back(
2769 (std::wstring)L"<"+name+L"> "+message);
2773 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2775 /*infostream<<"Client::addUpdateMeshTask(): "
2776 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2777 <<" ack_to_server="<<ack_to_server
2778 <<" urgent="<<urgent
2781 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2786 Create a task to update the mesh of the block
2789 MeshMakeData *data = new MeshMakeData(this);
2792 //TimeTaker timer("data fill");
2794 // Debug: 1-6ms, avg=2ms
2796 data->setCrack(m_crack_level, m_crack_pos);
2797 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2801 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2803 // Add task to queue
2804 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2806 /*infostream<<"Mesh update input queue size is "
2807 <<m_mesh_update_thread.m_queue_in.size()
2811 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2815 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2816 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2821 v3s16 p = blockpos + v3s16(0,0,0);
2822 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2823 addUpdateMeshTask(p, ack_to_server, urgent);
2825 catch(InvalidPositionException &e){}
2827 for (int i=0;i<6;i++)
2830 v3s16 p = blockpos + g_6dirs[i];
2831 addUpdateMeshTask(p, false, urgent);
2833 catch(InvalidPositionException &e){}
2837 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2841 infostream<<"Client::addUpdateMeshTaskForNode(): "
2842 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2846 v3s16 blockpos = getNodeBlockPos(nodepos);
2847 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2850 v3s16 p = blockpos + v3s16(0,0,0);
2851 addUpdateMeshTask(p, ack_to_server, urgent);
2853 catch(InvalidPositionException &e){}
2855 if(nodepos.X == blockpos_relative.X){
2857 v3s16 p = blockpos + v3s16(-1,0,0);
2858 addUpdateMeshTask(p, false, urgent);
2860 catch(InvalidPositionException &e){}
2862 if(nodepos.Y == blockpos_relative.Y){
2864 v3s16 p = blockpos + v3s16(0,-1,0);
2865 addUpdateMeshTask(p, false, urgent);
2867 catch(InvalidPositionException &e){}
2869 if(nodepos.Z == blockpos_relative.Z){
2871 v3s16 p = blockpos + v3s16(0,0,-1);
2872 addUpdateMeshTask(p, false, urgent);
2874 catch(InvalidPositionException &e){}
2878 ClientEvent Client::getClientEvent()
2880 if(m_client_event_queue.size() == 0)
2883 event.type = CE_NONE;
2886 return m_client_event_queue.pop_front();
2889 void draw_load_screen(const std::wstring &text,
2890 IrrlichtDevice* device, gui::IGUIFont* font,
2891 float dtime=0 ,int percent=0, bool clouds=true);
2892 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2894 infostream<<"Client::afterContentReceived() started"<<std::endl;
2895 assert(m_itemdef_received);
2896 assert(m_nodedef_received);
2897 assert(texturesReceived());
2899 // remove the information about which checksum each texture
2901 m_media_name_sha1_map.clear();
2903 // Rebuild inherited images and recreate textures
2904 infostream<<"- Rebuilding images and textures"<<std::endl;
2905 m_tsrc->rebuildImagesAndTextures();
2908 infostream<<"- Rebuilding shaders"<<std::endl;
2909 m_shsrc->rebuildShaders();
2911 // Update node aliases
2912 infostream<<"- Updating node aliases"<<std::endl;
2913 m_nodedef->updateAliases(m_itemdef);
2915 // Update node textures
2916 infostream<<"- Updating node textures"<<std::endl;
2917 m_nodedef->updateTextures(m_tsrc);
2919 // Preload item textures and meshes if configured to
2920 if(g_settings->getBool("preload_item_visuals"))
2922 verbosestream<<"Updating item textures and meshes"<<std::endl;
2923 wchar_t* text = wgettext("Item textures...");
2924 draw_load_screen(text,device,font,0,0);
2925 std::set<std::string> names = m_itemdef->getAll();
2926 size_t size = names.size();
2929 for(std::set<std::string>::const_iterator
2930 i = names.begin(); i != names.end(); ++i){
2931 // Asking for these caches the result
2932 m_itemdef->getInventoryTexture(*i, this);
2933 m_itemdef->getWieldMesh(*i, this);
2935 percent = count*100/size;
2936 if (count%50 == 0) // only update every 50 item
2937 draw_load_screen(text,device,font,0,percent);
2942 // Start mesh update thread after setting up content definitions
2943 infostream<<"- Starting mesh update thread"<<std::endl;
2944 m_mesh_update_thread.Start();
2946 infostream<<"Client::afterContentReceived() done"<<std::endl;
2949 float Client::getRTT(void)
2952 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2953 } catch(con::PeerNotFoundException &e){
2958 // IGameDef interface
2960 IItemDefManager* Client::getItemDefManager()
2964 INodeDefManager* Client::getNodeDefManager()
2968 ICraftDefManager* Client::getCraftDefManager()
2971 //return m_craftdef;
2973 ITextureSource* Client::getTextureSource()
2977 IShaderSource* Client::getShaderSource()
2981 u16 Client::allocateUnknownNodeId(const std::string &name)
2983 errorstream<<"Client::allocateUnknownNodeId(): "
2984 <<"Client cannot allocate node IDs"<<std::endl;
2986 return CONTENT_IGNORE;
2988 ISoundManager* Client::getSoundManager()
2992 MtEventManager* Client::getEventManager()