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
282 m_mesh_update_thread(this),
284 new ClientMap(this, this, control,
285 device->getSceneManager()->getRootSceneNode(),
286 device->getSceneManager(), 666),
287 device->getSceneManager(),
290 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
292 m_server_ser_ver(SER_FMT_VER_INVALID),
294 m_inventory_updated(false),
295 m_inventory_from_server(NULL),
296 m_inventory_from_server_age(0.0),
301 m_password(password),
302 m_access_denied(false),
303 m_media_cache(getMediaCacheDir()),
304 m_media_receive_started(false),
306 m_media_received_count(0),
307 m_itemdef_received(false),
308 m_nodedef_received(false),
309 m_time_of_day_set(false),
310 m_last_time_of_day_f(-1),
311 m_time_of_day_update_timer(0),
312 m_recommended_send_interval(0.1),
313 m_removed_sounds_check_timer(0)
315 m_packetcounter_timer = 0.0;
316 //m_delete_unused_sectors_timer = 0.0;
317 m_connection_reinit_timer = 0.0;
318 m_avg_rtt_timer = 0.0;
319 m_playerpos_send_timer = 0.0;
320 m_ignore_damage_timer = 0.0;
322 // Build main texture atlas, now that the GameDef exists (that is, us)
323 if(g_settings->getBool("enable_texture_atlas"))
324 m_tsrc->buildMainAtlas(this);
326 infostream<<"Not building texture atlas."<<std::endl;
332 Player *player = new LocalPlayer(this);
334 player->updateName(playername);
336 m_env.addPlayer(player);
339 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
340 m_media_fetch_threads.push_back(new MediaFetchThread(this));
346 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
350 m_mesh_update_thread.setRun(false);
351 while(m_mesh_update_thread.IsRunning())
353 while(!m_mesh_update_thread.m_queue_out.empty()) {
354 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
359 delete m_inventory_from_server;
361 // Delete detached inventories
363 for(std::map<std::string, Inventory*>::iterator
364 i = m_detached_inventories.begin();
365 i != m_detached_inventories.end(); i++){
370 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
371 i != m_media_fetch_threads.end(); ++i)
374 // cleanup 3d model meshes on client shutdown
375 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
376 scene::IAnimatedMesh * mesh =
377 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
380 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
384 void Client::connect(Address address)
386 DSTACK(__FUNCTION_NAME);
387 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
388 m_con.SetTimeoutMs(0);
389 m_con.Connect(address);
392 bool Client::connectedAndInitialized()
394 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
396 if(m_con.Connected() == false)
399 if(m_server_ser_ver == SER_FMT_VER_INVALID)
405 void Client::step(float dtime)
407 DSTACK(__FUNCTION_NAME);
413 if(m_ignore_damage_timer > dtime)
414 m_ignore_damage_timer -= dtime;
416 m_ignore_damage_timer = 0.0;
418 m_animation_time += dtime;
419 if(m_animation_time > 60.0)
420 m_animation_time -= 60.0;
422 m_time_of_day_update_timer += dtime;
424 //infostream<<"Client steps "<<dtime<<std::endl;
427 //TimeTaker timer("ReceiveAll()", m_device);
433 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
435 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
436 m_con.RunTimeouts(dtime);
443 float &counter = m_packetcounter_timer;
449 infostream<<"Client packetcounter (20s):"<<std::endl;
450 m_packetcounter.print(infostream);
451 m_packetcounter.clear();
455 // Get connection status
456 bool connected = connectedAndInitialized();
461 Delete unused sectors
463 NOTE: This jams the game for a while because deleting sectors
467 float &counter = m_delete_unused_sectors_timer;
475 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
477 core::list<v3s16> deleted_blocks;
479 float delete_unused_sectors_timeout =
480 g_settings->getFloat("client_delete_unused_sectors_timeout");
482 // Delete sector blocks
483 /*u32 num = m_env.getMap().unloadUnusedData
484 (delete_unused_sectors_timeout,
485 true, &deleted_blocks);*/
487 // Delete whole sectors
488 m_env.getMap().unloadUnusedData
489 (delete_unused_sectors_timeout,
492 if(deleted_blocks.size() > 0)
494 /*infostream<<"Client: Deleted blocks of "<<num
495 <<" unused sectors"<<std::endl;*/
496 /*infostream<<"Client: Deleted "<<num
497 <<" unused sectors"<<std::endl;*/
503 // Env is locked so con can be locked.
504 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
506 core::list<v3s16>::Iterator i = deleted_blocks.begin();
507 core::list<v3s16> sendlist;
510 if(sendlist.size() == 255 || i == deleted_blocks.end())
512 if(sendlist.size() == 0)
521 u32 replysize = 2+1+6*sendlist.size();
522 SharedBuffer<u8> reply(replysize);
523 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
524 reply[2] = sendlist.size();
526 for(core::list<v3s16>::Iterator
527 j = sendlist.begin();
528 j != sendlist.end(); j++)
530 writeV3S16(&reply[2+1+6*k], *j);
533 m_con.Send(PEER_ID_SERVER, 1, reply, true);
535 if(i == deleted_blocks.end())
541 sendlist.push_back(*i);
549 if(connected == false)
551 float &counter = m_connection_reinit_timer;
557 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
559 Player *myplayer = m_env.getLocalPlayer();
560 assert(myplayer != NULL);
562 // Send TOSERVER_INIT
563 // [0] u16 TOSERVER_INIT
564 // [2] u8 SER_FMT_VER_HIGHEST
565 // [3] u8[20] player_name
566 // [23] u8[28] password (new in some version)
567 // [51] u16 minimum supported network protocol version (added sometime)
568 // [53] u16 maximum supported network protocol version (added later than the previous one)
569 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
570 writeU16(&data[0], TOSERVER_INIT);
571 writeU8(&data[2], SER_FMT_VER_HIGHEST);
573 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
574 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
576 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
579 memset((char*)&data[23], 0, PASSWORD_SIZE);
580 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
582 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
583 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
585 // Send as unreliable
586 Send(0, data, false);
589 // Not connected, return
594 Do stuff if connected
598 Run Map's timers and unload unused data
600 const float map_timer_and_unload_dtime = 5.25;
601 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
603 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
604 std::list<v3s16> deleted_blocks;
605 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
606 g_settings->getFloat("client_unload_unused_data_timeout"),
609 /*if(deleted_blocks.size() > 0)
610 infostream<<"Client: Unloaded "<<deleted_blocks.size()
611 <<" unused blocks"<<std::endl;*/
615 NOTE: This loop is intentionally iterated the way it is.
618 std::list<v3s16>::iterator i = deleted_blocks.begin();
619 std::list<v3s16> sendlist;
622 if(sendlist.size() == 255 || i == deleted_blocks.end())
624 if(sendlist.size() == 0)
633 u32 replysize = 2+1+6*sendlist.size();
634 SharedBuffer<u8> reply(replysize);
635 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
636 reply[2] = sendlist.size();
638 for(std::list<v3s16>::iterator
639 j = sendlist.begin();
640 j != sendlist.end(); ++j)
642 writeV3S16(&reply[2+1+6*k], *j);
645 m_con.Send(PEER_ID_SERVER, 1, reply, true);
647 if(i == deleted_blocks.end())
653 sendlist.push_back(*i);
663 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
665 // Control local player (0ms)
666 LocalPlayer *player = m_env.getLocalPlayer();
667 assert(player != NULL);
668 player->applyControl(dtime);
670 //TimeTaker envtimer("env step", m_device);
679 ClientEnvEvent event = m_env.getClientEvent();
680 if(event.type == CEE_NONE)
684 else if(event.type == CEE_PLAYER_DAMAGE)
686 if(m_ignore_damage_timer <= 0)
688 u8 damage = event.player_damage.amount;
690 if(event.player_damage.send_to_server)
693 // Add to ClientEvent queue
695 event.type = CE_PLAYER_DAMAGE;
696 event.player_damage.amount = damage;
697 m_client_event_queue.push_back(event);
707 float &counter = m_avg_rtt_timer;
712 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
713 // connectedAndInitialized() is true, peer exists.
714 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
715 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
720 Send player position to server
723 float &counter = m_playerpos_send_timer;
725 if(counter >= m_recommended_send_interval)
733 Replace updated meshes
736 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
738 //TimeTaker timer("** Processing mesh update result queue");
741 /*infostream<<"Mesh update result queue size is "
742 <<m_mesh_update_thread.m_queue_out.size()
745 int num_processed_meshes = 0;
746 while(!m_mesh_update_thread.m_queue_out.empty())
748 num_processed_meshes++;
749 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
750 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
753 //JMutexAutoLock lock(block->mesh_mutex);
755 // Delete the old mesh
756 if(block->mesh != NULL)
758 // TODO: Remove hardware buffers of meshbuffers of block->mesh
763 // Replace with the new mesh
764 block->mesh = r.mesh;
768 if(r.ack_block_to_server)
770 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
771 <<","<<r.p.Z<<")"<<std::endl;*/
782 u32 replysize = 2+1+6;
783 SharedBuffer<u8> reply(replysize);
784 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
786 writeV3S16(&reply[3], r.p);
788 m_con.Send(PEER_ID_SERVER, 1, reply, true);
791 if(num_processed_meshes > 0)
792 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
798 if (m_media_receive_started) {
799 bool all_stopped = true;
800 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
801 thread != m_media_fetch_threads.end(); ++thread) {
802 all_stopped &= !(*thread)->IsRunning();
803 while (!(*thread)->m_file_data.empty()) {
804 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
805 ++m_media_received_count;
807 bool success = loadMedia(out.second, out.first);
809 verbosestream<<"Client: Loaded received media: "
810 <<"\""<<out.first<<"\". Caching."<<std::endl;
812 infostream<<"Client: Failed to load received media: "
813 <<"\""<<out.first<<"\". Not caching."<<std::endl;
817 bool did = fs::CreateAllDirs(getMediaCacheDir());
819 errorstream<<"Could not create media cache directory"
824 std::map<std::string, std::string>::iterator n;
825 n = m_media_name_sha1_map.find(out.first);
826 if(n == m_media_name_sha1_map.end())
827 errorstream<<"The server sent a file that has not "
828 <<"been announced."<<std::endl;
830 m_media_cache.update_sha1(out.second);
835 std::list<MediaRequest> fetch_failed;
836 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
837 thread != m_media_fetch_threads.end(); ++thread) {
838 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
839 request != (*thread)->m_failed.end(); ++request)
840 fetch_failed.push_back(*request);
841 (*thread)->m_failed.clear();
843 if (fetch_failed.size() > 0) {
844 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
845 << "Requesting them the usual way." << std::endl;
846 request_media(fetch_failed);
852 If the server didn't update the inventory in a while, revert
853 the local inventory (so the player notices the lag problem
854 and knows something is wrong).
856 if(m_inventory_from_server)
858 float interval = 10.0;
859 float count_before = floor(m_inventory_from_server_age / interval);
861 m_inventory_from_server_age += dtime;
863 float count_after = floor(m_inventory_from_server_age / interval);
865 if(count_after != count_before)
867 // Do this every <interval> seconds after TOCLIENT_INVENTORY
868 // Reset the locally changed inventory to the authoritative inventory
869 Player *player = m_env.getLocalPlayer();
870 player->inventory = *m_inventory_from_server;
871 m_inventory_updated = true;
876 Update positions of sounds attached to objects
879 for(std::map<int, u16>::iterator
880 i = m_sounds_to_objects.begin();
881 i != m_sounds_to_objects.end(); i++)
883 int client_id = i->first;
884 u16 object_id = i->second;
885 ClientActiveObject *cao = m_env.getActiveObject(object_id);
888 v3f pos = cao->getPosition();
889 m_sound->updateSoundPosition(client_id, pos);
894 Handle removed remotely initiated sounds
896 m_removed_sounds_check_timer += dtime;
897 if(m_removed_sounds_check_timer >= 2.32)
899 m_removed_sounds_check_timer = 0;
900 // Find removed sounds and clear references to them
901 std::set<s32> removed_server_ids;
902 for(std::map<s32, int>::iterator
903 i = m_sounds_server_to_client.begin();
904 i != m_sounds_server_to_client.end();)
906 s32 server_id = i->first;
907 int client_id = i->second;
909 if(!m_sound->soundExists(client_id)){
910 m_sounds_server_to_client.erase(server_id);
911 m_sounds_client_to_server.erase(client_id);
912 m_sounds_to_objects.erase(client_id);
913 removed_server_ids.insert(server_id);
917 if(removed_server_ids.size() != 0)
919 std::ostringstream os(std::ios_base::binary);
920 writeU16(os, TOSERVER_REMOVED_SOUNDS);
921 writeU16(os, removed_server_ids.size());
922 for(std::set<s32>::iterator i = removed_server_ids.begin();
923 i != removed_server_ids.end(); i++)
925 std::string s = os.str();
926 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
933 bool Client::loadMedia(const std::string &data, const std::string &filename)
935 // Silly irrlicht's const-incorrectness
936 Buffer<char> data_rw(data.c_str(), data.size());
940 const char *image_ext[] = {
941 ".png", ".jpg", ".bmp", ".tga",
942 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
945 name = removeStringEnd(filename, image_ext);
948 verbosestream<<"Client: Attempting to load image "
949 <<"file \""<<filename<<"\""<<std::endl;
951 io::IFileSystem *irrfs = m_device->getFileSystem();
952 video::IVideoDriver *vdrv = m_device->getVideoDriver();
954 // Create an irrlicht memory file
955 io::IReadFile *rfile = irrfs->createMemoryReadFile(
956 *data_rw, data_rw.getSize(), "_tempreadfile");
959 video::IImage *img = vdrv->createImageFromFile(rfile);
961 errorstream<<"Client: Cannot create image from data of "
962 <<"file \""<<filename<<"\""<<std::endl;
967 m_tsrc->insertSourceImage(filename, img);
974 const char *sound_ext[] = {
975 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
976 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
979 name = removeStringEnd(filename, sound_ext);
982 verbosestream<<"Client: Attempting to load sound "
983 <<"file \""<<filename<<"\""<<std::endl;
984 m_sound->loadSoundData(name, data);
988 const char *model_ext[] = {
989 ".x", ".b3d", ".md2", ".obj",
992 name = removeStringEnd(filename, model_ext);
995 verbosestream<<"Client: Storing model into Irrlicht: "
996 <<"\""<<filename<<"\""<<std::endl;
997 scene::ISceneManager *smgr = m_device->getSceneManager();
999 //check if mesh was already cached
1000 scene::IAnimatedMesh *mesh =
1001 smgr->getMeshCache()->getMeshByName(filename.c_str());
1004 errorstream << "Multiple models with name: " << filename.c_str() <<
1005 " found replacing previous model!" << std::endl;
1007 smgr->getMeshCache()->removeMesh(mesh);
1011 io::IFileSystem *irrfs = m_device->getFileSystem();
1012 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1013 *data_rw, data_rw.getSize(), filename.c_str());
1016 mesh = smgr->getMesh(rfile);
1017 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1022 errorstream<<"Client: Don't know how to load file \""
1023 <<filename<<"\""<<std::endl;
1027 // Virtual methods from con::PeerHandler
1028 void Client::peerAdded(con::Peer *peer)
1030 infostream<<"Client::peerAdded(): peer->id="
1031 <<peer->id<<std::endl;
1033 void Client::deletingPeer(con::Peer *peer, bool timeout)
1035 infostream<<"Client::deletingPeer(): "
1036 "Server Peer is getting deleted "
1037 <<"(timeout="<<timeout<<")"<<std::endl;
1042 u16 number of files requested
1048 void Client::request_media(const std::list<MediaRequest> &file_requests)
1050 std::ostringstream os(std::ios_base::binary);
1051 writeU16(os, TOSERVER_REQUEST_MEDIA);
1052 writeU16(os, file_requests.size());
1054 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1055 i != file_requests.end(); ++i) {
1056 os<<serializeString(i->name);
1060 std::string s = os.str();
1061 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1063 Send(0, data, true);
1064 infostream<<"Client: Sending media request list to server ("
1065 <<file_requests.size()<<" files)"<<std::endl;
1068 void Client::ReceiveAll()
1070 DSTACK(__FUNCTION_NAME);
1071 u32 start_ms = porting::getTimeMs();
1074 // Limit time even if there would be huge amounts of data to
1076 if(porting::getTimeMs() > start_ms + 100)
1081 g_profiler->graphAdd("client_received_packets", 1);
1083 catch(con::NoIncomingDataException &e)
1087 catch(con::InvalidIncomingDataException &e)
1089 infostream<<"Client::ReceiveAll(): "
1090 "InvalidIncomingDataException: what()="
1091 <<e.what()<<std::endl;
1096 void Client::Receive()
1098 DSTACK(__FUNCTION_NAME);
1099 SharedBuffer<u8> data;
1103 //TimeTaker t1("con mutex and receive", m_device);
1104 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1105 datasize = m_con.Receive(sender_peer_id, data);
1107 //TimeTaker t1("ProcessData", m_device);
1108 ProcessData(*data, datasize, sender_peer_id);
1112 sender_peer_id given to this shall be quaranteed to be a valid peer
1114 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1116 DSTACK(__FUNCTION_NAME);
1118 // Ignore packets that don't even fit a command
1121 m_packetcounter.add(60000);
1125 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1127 //infostream<<"Client: received command="<<command<<std::endl;
1128 m_packetcounter.add((u16)command);
1131 If this check is removed, be sure to change the queue
1132 system to know the ids
1134 if(sender_peer_id != PEER_ID_SERVER)
1136 infostream<<"Client::ProcessData(): Discarding data not "
1137 "coming from server: peer_id="<<sender_peer_id
1142 u8 ser_version = m_server_ser_ver;
1144 //infostream<<"Client received command="<<(int)command<<std::endl;
1146 if(command == TOCLIENT_INIT)
1151 u8 deployed = data[2];
1153 infostream<<"Client: TOCLIENT_INIT received with "
1154 "deployed="<<((int)deployed&0xff)<<std::endl;
1156 if(deployed < SER_FMT_VER_LOWEST
1157 || deployed > SER_FMT_VER_HIGHEST)
1159 infostream<<"Client: TOCLIENT_INIT: Server sent "
1160 <<"unsupported ser_fmt_ver"<<std::endl;
1164 m_server_ser_ver = deployed;
1166 // Get player position
1167 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1168 if(datasize >= 2+1+6)
1169 playerpos_s16 = readV3S16(&data[2+1]);
1170 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1173 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1175 // Set player position
1176 Player *player = m_env.getLocalPlayer();
1177 assert(player != NULL);
1178 player->setPosition(playerpos_f);
1181 if(datasize >= 2+1+6+8)
1184 m_map_seed = readU64(&data[2+1+6]);
1185 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1188 if(datasize >= 2+1+6+8+4)
1191 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1192 infostream<<"Client: received recommended send interval "
1193 <<m_recommended_send_interval<<std::endl;
1198 SharedBuffer<u8> reply(replysize);
1199 writeU16(&reply[0], TOSERVER_INIT2);
1201 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1206 if(command == TOCLIENT_ACCESS_DENIED)
1208 // The server didn't like our password. Note, this needs
1209 // to be processed even if the serialisation format has
1210 // not been agreed yet, the same as TOCLIENT_INIT.
1211 m_access_denied = true;
1212 m_access_denied_reason = L"Unknown";
1215 std::string datastring((char*)&data[2], datasize-2);
1216 std::istringstream is(datastring, std::ios_base::binary);
1217 m_access_denied_reason = deSerializeWideString(is);
1222 if(ser_version == SER_FMT_VER_INVALID)
1224 infostream<<"Client: Server serialization"
1225 " format invalid or not initialized."
1226 " Skipping incoming command="<<command<<std::endl;
1230 // Just here to avoid putting the two if's together when
1231 // making some copypasta
1234 if(command == TOCLIENT_REMOVENODE)
1239 p.X = readS16(&data[2]);
1240 p.Y = readS16(&data[4]);
1241 p.Z = readS16(&data[6]);
1243 //TimeTaker t1("TOCLIENT_REMOVENODE");
1247 else if(command == TOCLIENT_ADDNODE)
1249 if(datasize < 8 + MapNode::serializedLength(ser_version))
1253 p.X = readS16(&data[2]);
1254 p.Y = readS16(&data[4]);
1255 p.Z = readS16(&data[6]);
1257 //TimeTaker t1("TOCLIENT_ADDNODE");
1260 n.deSerialize(&data[8], ser_version);
1264 else if(command == TOCLIENT_BLOCKDATA)
1266 // Ignore too small packet
1271 p.X = readS16(&data[2]);
1272 p.Y = readS16(&data[4]);
1273 p.Z = readS16(&data[6]);
1275 /*infostream<<"Client: Thread: BLOCKDATA for ("
1276 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1277 /*infostream<<"Client: Thread: BLOCKDATA for ("
1278 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1280 std::string datastring((char*)&data[8], datasize-8);
1281 std::istringstream istr(datastring, std::ios_base::binary);
1286 v2s16 p2d(p.X, p.Z);
1287 sector = m_env.getMap().emergeSector(p2d);
1289 assert(sector->getPos() == p2d);
1291 //TimeTaker timer("MapBlock deSerialize");
1294 block = sector->getBlockNoCreateNoEx(p.Y);
1298 Update an existing block
1300 //infostream<<"Updating"<<std::endl;
1301 block->deSerialize(istr, ser_version, false);
1308 //infostream<<"Creating new"<<std::endl;
1309 block = new MapBlock(&m_env.getMap(), p, this);
1310 block->deSerialize(istr, ser_version, false);
1311 sector->insertBlock(block);
1325 u32 replysize = 2+1+6;
1326 SharedBuffer<u8> reply(replysize);
1327 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1329 writeV3S16(&reply[3], p);
1331 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1335 Add it to mesh update queue and set it to be acknowledged after update.
1337 //infostream<<"Adding mesh update task for received block"<<std::endl;
1338 addUpdateMeshTaskWithEdge(p, true);
1340 else if(command == TOCLIENT_INVENTORY)
1345 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1348 //TimeTaker t2("mutex locking", m_device);
1349 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1352 //TimeTaker t3("istringstream init", m_device);
1353 std::string datastring((char*)&data[2], datasize-2);
1354 std::istringstream is(datastring, std::ios_base::binary);
1357 //m_env.printPlayers(infostream);
1359 //TimeTaker t4("player get", m_device);
1360 Player *player = m_env.getLocalPlayer();
1361 assert(player != NULL);
1364 //TimeTaker t1("inventory.deSerialize()", m_device);
1365 player->inventory.deSerialize(is);
1368 m_inventory_updated = true;
1370 delete m_inventory_from_server;
1371 m_inventory_from_server = new Inventory(player->inventory);
1372 m_inventory_from_server_age = 0.0;
1374 //infostream<<"Client got player inventory:"<<std::endl;
1375 //player->inventory.print(infostream);
1378 else if(command == TOCLIENT_TIME_OF_DAY)
1383 u16 time_of_day = readU16(&data[2]);
1384 time_of_day = time_of_day % 24000;
1385 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1386 float time_speed = 0;
1387 if(datasize >= 2 + 2 + 4){
1388 time_speed = readF1000(&data[4]);
1390 // Old message; try to approximate speed of time by ourselves
1391 float time_of_day_f = (float)time_of_day / 24000.0;
1392 float tod_diff_f = 0;
1393 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1394 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1396 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1397 m_last_time_of_day_f = time_of_day_f;
1398 float time_diff = m_time_of_day_update_timer;
1399 m_time_of_day_update_timer = 0;
1400 if(m_time_of_day_set){
1401 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1402 infostream<<"Client: Measured time_of_day speed (old format): "
1403 <<time_speed<<" tod_diff_f="<<tod_diff_f
1404 <<" time_diff="<<time_diff<<std::endl;
1408 // Update environment
1409 m_env.setTimeOfDay(time_of_day);
1410 m_env.setTimeOfDaySpeed(time_speed);
1411 m_time_of_day_set = true;
1413 u32 dr = m_env.getDayNightRatio();
1414 verbosestream<<"Client: time_of_day="<<time_of_day
1415 <<" time_speed="<<time_speed
1416 <<" dr="<<dr<<std::endl;
1418 else if(command == TOCLIENT_CHAT_MESSAGE)
1426 std::string datastring((char*)&data[2], datasize-2);
1427 std::istringstream is(datastring, std::ios_base::binary);
1430 is.read((char*)buf, 2);
1431 u16 len = readU16(buf);
1433 std::wstring message;
1434 for(u16 i=0; i<len; i++)
1436 is.read((char*)buf, 2);
1437 message += (wchar_t)readU16(buf);
1440 /*infostream<<"Client received chat message: "
1441 <<wide_to_narrow(message)<<std::endl;*/
1443 m_chat_queue.push_back(message);
1445 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1447 //if(g_settings->getBool("enable_experimental"))
1451 u16 count of removed objects
1452 for all removed objects {
1455 u16 count of added objects
1456 for all added objects {
1459 u32 initialization data length
1460 string initialization data
1465 // Get all data except the command number
1466 std::string datastring((char*)&data[2], datasize-2);
1467 // Throw them in an istringstream
1468 std::istringstream is(datastring, std::ios_base::binary);
1472 // Read removed objects
1474 u16 removed_count = readU16((u8*)buf);
1475 for(u16 i=0; i<removed_count; i++)
1478 u16 id = readU16((u8*)buf);
1481 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1482 m_env.removeActiveObject(id);
1486 // Read added objects
1488 u16 added_count = readU16((u8*)buf);
1489 for(u16 i=0; i<added_count; i++)
1492 u16 id = readU16((u8*)buf);
1494 u8 type = readU8((u8*)buf);
1495 std::string data = deSerializeLongString(is);
1498 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1499 m_env.addActiveObject(id, type, data);
1504 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1506 //if(g_settings->getBool("enable_experimental"))
1518 // Get all data except the command number
1519 std::string datastring((char*)&data[2], datasize-2);
1520 // Throw them in an istringstream
1521 std::istringstream is(datastring, std::ios_base::binary);
1523 while(is.eof() == false)
1527 u16 id = readU16((u8*)buf);
1531 u16 message_size = readU16((u8*)buf);
1532 std::string message;
1533 message.reserve(message_size);
1534 for(u16 i=0; i<message_size; i++)
1537 message.append(buf, 1);
1539 // Pass on to the environment
1541 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1542 m_env.processActiveObjectMessage(id, message);
1547 else if(command == TOCLIENT_MOVEMENT)
1549 std::string datastring((char*)&data[2], datasize-2);
1550 std::istringstream is(datastring, std::ios_base::binary);
1551 Player *player = m_env.getLocalPlayer();
1552 assert(player != NULL);
1554 player->movement_acceleration_default = readF1000(is) * BS;
1555 player->movement_acceleration_air = readF1000(is) * BS;
1556 player->movement_acceleration_fast = readF1000(is) * BS;
1557 player->movement_speed_walk = readF1000(is) * BS;
1558 player->movement_speed_crouch = readF1000(is) * BS;
1559 player->movement_speed_fast = readF1000(is) * BS;
1560 player->movement_speed_climb = readF1000(is) * BS;
1561 player->movement_speed_jump = readF1000(is) * BS;
1562 player->movement_liquid_fluidity = readF1000(is) * BS;
1563 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1564 player->movement_liquid_sink = readF1000(is) * BS;
1565 player->movement_gravity = readF1000(is) * BS;
1567 else if(command == TOCLIENT_HP)
1569 std::string datastring((char*)&data[2], datasize-2);
1570 std::istringstream is(datastring, std::ios_base::binary);
1571 Player *player = m_env.getLocalPlayer();
1572 assert(player != NULL);
1573 u8 oldhp = player->hp;
1579 // Add to ClientEvent queue
1581 event.type = CE_PLAYER_DAMAGE;
1582 event.player_damage.amount = oldhp - hp;
1583 m_client_event_queue.push_back(event);
1586 else if(command == TOCLIENT_MOVE_PLAYER)
1588 std::string datastring((char*)&data[2], datasize-2);
1589 std::istringstream is(datastring, std::ios_base::binary);
1590 Player *player = m_env.getLocalPlayer();
1591 assert(player != NULL);
1592 v3f pos = readV3F1000(is);
1593 f32 pitch = readF1000(is);
1594 f32 yaw = readF1000(is);
1595 player->setPosition(pos);
1596 /*player->setPitch(pitch);
1597 player->setYaw(yaw);*/
1599 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1600 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1606 Add to ClientEvent queue.
1607 This has to be sent to the main program because otherwise
1608 it would just force the pitch and yaw values to whatever
1609 the camera points to.
1612 event.type = CE_PLAYER_FORCE_MOVE;
1613 event.player_force_move.pitch = pitch;
1614 event.player_force_move.yaw = yaw;
1615 m_client_event_queue.push_back(event);
1617 // Ignore damage for a few seconds, so that the player doesn't
1618 // get damage from falling on ground
1619 m_ignore_damage_timer = 3.0;
1621 else if(command == TOCLIENT_PLAYERITEM)
1623 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1625 else if(command == TOCLIENT_DEATHSCREEN)
1627 std::string datastring((char*)&data[2], datasize-2);
1628 std::istringstream is(datastring, std::ios_base::binary);
1630 bool set_camera_point_target = readU8(is);
1631 v3f camera_point_target = readV3F1000(is);
1634 event.type = CE_DEATHSCREEN;
1635 event.deathscreen.set_camera_point_target = set_camera_point_target;
1636 event.deathscreen.camera_point_target_x = camera_point_target.X;
1637 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1638 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1639 m_client_event_queue.push_back(event);
1641 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1643 std::string datastring((char*)&data[2], datasize-2);
1644 std::istringstream is(datastring, std::ios_base::binary);
1646 // Mesh update thread must be stopped while
1647 // updating content definitions
1648 assert(!m_mesh_update_thread.IsRunning());
1650 int num_files = readU16(is);
1652 infostream<<"Client: Received media announcement: packet size: "
1653 <<datasize<<std::endl;
1655 std::list<MediaRequest> file_requests;
1657 for(int i=0; i<num_files; i++)
1659 //read file from cache
1660 std::string name = deSerializeString(is);
1661 std::string sha1_base64 = deSerializeString(is);
1663 // if name contains illegal characters, ignore the file
1664 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1665 errorstream<<"Client: ignoring illegal file name "
1666 <<"sent by server: \""<<name<<"\""<<std::endl;
1670 std::string sha1_raw = base64_decode(sha1_base64);
1671 std::string sha1_hex = hex_encode(sha1_raw);
1672 std::ostringstream tmp_os(std::ios_base::binary);
1673 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1674 m_media_name_sha1_map[name] = sha1_raw;
1676 // If found in cache, try to load it from there
1679 bool success = loadMedia(tmp_os.str(), name);
1681 verbosestream<<"Client: Loaded cached media: "
1682 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1685 infostream<<"Client: Failed to load cached media: "
1686 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1689 // Didn't load from cache; queue it to be requested
1690 verbosestream<<"Client: Adding file to request list: \""
1691 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1692 file_requests.push_back(MediaRequest(name));
1695 std::string remote_media = "";
1697 remote_media = deSerializeString(is);
1699 catch(SerializationError) {
1700 // not supported by server or turned off
1703 m_media_count = file_requests.size();
1704 m_media_receive_started = true;
1706 if (remote_media == "" || !USE_CURL) {
1707 request_media(file_requests);
1710 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1711 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1712 i != file_requests.end(); ++i) {
1713 (*cur)->m_file_requests.push_back(*i);
1715 if (cur == m_media_fetch_threads.end())
1716 cur = m_media_fetch_threads.begin();
1718 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1719 i != m_media_fetch_threads.end(); ++i) {
1720 (*i)->m_remote_url = remote_media;
1725 // notify server we received everything
1726 std::ostringstream os(std::ios_base::binary);
1727 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1728 std::string s = os.str();
1729 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1731 Send(0, data, true);
1734 event.type = CE_TEXTURES_UPDATED;
1735 m_client_event_queue.push_back(event);
1737 else if(command == TOCLIENT_MEDIA)
1739 if (m_media_count == 0)
1741 std::string datastring((char*)&data[2], datasize-2);
1742 std::istringstream is(datastring, std::ios_base::binary);
1744 // Mesh update thread must be stopped while
1745 // updating content definitions
1746 assert(!m_mesh_update_thread.IsRunning());
1750 u16 total number of file bunches
1751 u16 index of this bunch
1752 u32 number of files in this bunch
1760 int num_bunches = readU16(is);
1761 int bunch_i = readU16(is);
1762 int num_files = readU32(is);
1763 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1764 <<num_bunches<<" files="<<num_files
1765 <<" size="<<datasize<<std::endl;
1766 for(int i=0; i<num_files; i++){
1767 m_media_received_count++;
1768 std::string name = deSerializeString(is);
1769 std::string data = deSerializeLongString(is);
1771 // if name contains illegal characters, ignore the file
1772 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1773 errorstream<<"Client: ignoring illegal file name "
1774 <<"sent by server: \""<<name<<"\""<<std::endl;
1778 bool success = loadMedia(data, name);
1780 verbosestream<<"Client: Loaded received media: "
1781 <<"\""<<name<<"\". Caching."<<std::endl;
1783 infostream<<"Client: Failed to load received media: "
1784 <<"\""<<name<<"\". Not caching."<<std::endl;
1788 bool did = fs::CreateAllDirs(getMediaCacheDir());
1790 errorstream<<"Could not create media cache directory"
1795 std::map<std::string, std::string>::iterator n;
1796 n = m_media_name_sha1_map.find(name);
1797 if(n == m_media_name_sha1_map.end())
1798 errorstream<<"The server sent a file that has not "
1799 <<"been announced."<<std::endl;
1801 m_media_cache.update_sha1(data);
1806 event.type = CE_TEXTURES_UPDATED;
1807 m_client_event_queue.push_back(event);
1809 else if(command == TOCLIENT_TOOLDEF)
1811 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1813 else if(command == TOCLIENT_NODEDEF)
1815 infostream<<"Client: Received node definitions: packet size: "
1816 <<datasize<<std::endl;
1818 // Mesh update thread must be stopped while
1819 // updating content definitions
1820 assert(!m_mesh_update_thread.IsRunning());
1822 // Decompress node definitions
1823 std::string datastring((char*)&data[2], datasize-2);
1824 std::istringstream is(datastring, std::ios_base::binary);
1825 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1826 std::ostringstream tmp_os;
1827 decompressZlib(tmp_is, tmp_os);
1829 // Deserialize node definitions
1830 std::istringstream tmp_is2(tmp_os.str());
1831 m_nodedef->deSerialize(tmp_is2);
1832 m_nodedef_received = true;
1834 else if(command == TOCLIENT_CRAFTITEMDEF)
1836 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1838 else if(command == TOCLIENT_ITEMDEF)
1840 infostream<<"Client: Received item definitions: packet size: "
1841 <<datasize<<std::endl;
1843 // Mesh update thread must be stopped while
1844 // updating content definitions
1845 assert(!m_mesh_update_thread.IsRunning());
1847 // Decompress item definitions
1848 std::string datastring((char*)&data[2], datasize-2);
1849 std::istringstream is(datastring, std::ios_base::binary);
1850 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1851 std::ostringstream tmp_os;
1852 decompressZlib(tmp_is, tmp_os);
1854 // Deserialize node definitions
1855 std::istringstream tmp_is2(tmp_os.str());
1856 m_itemdef->deSerialize(tmp_is2);
1857 m_itemdef_received = true;
1859 else if(command == TOCLIENT_PLAY_SOUND)
1861 std::string datastring((char*)&data[2], datasize-2);
1862 std::istringstream is(datastring, std::ios_base::binary);
1864 s32 server_id = readS32(is);
1865 std::string name = deSerializeString(is);
1866 float gain = readF1000(is);
1867 int type = readU8(is); // 0=local, 1=positional, 2=object
1868 v3f pos = readV3F1000(is);
1869 u16 object_id = readU16(is);
1870 bool loop = readU8(is);
1875 client_id = m_sound->playSound(name, loop, gain);
1877 case 1: // positional
1878 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1881 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1883 pos = cao->getPosition();
1884 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1885 // TODO: Set up sound to move with object
1890 if(client_id != -1){
1891 m_sounds_server_to_client[server_id] = client_id;
1892 m_sounds_client_to_server[client_id] = server_id;
1894 m_sounds_to_objects[client_id] = object_id;
1897 else if(command == TOCLIENT_STOP_SOUND)
1899 std::string datastring((char*)&data[2], datasize-2);
1900 std::istringstream is(datastring, std::ios_base::binary);
1902 s32 server_id = readS32(is);
1903 std::map<s32, int>::iterator i =
1904 m_sounds_server_to_client.find(server_id);
1905 if(i != m_sounds_server_to_client.end()){
1906 int client_id = i->second;
1907 m_sound->stopSound(client_id);
1910 else if(command == TOCLIENT_PRIVILEGES)
1912 std::string datastring((char*)&data[2], datasize-2);
1913 std::istringstream is(datastring, std::ios_base::binary);
1915 m_privileges.clear();
1916 infostream<<"Client: Privileges updated: ";
1917 u16 num_privileges = readU16(is);
1918 for(u16 i=0; i<num_privileges; i++){
1919 std::string priv = deSerializeString(is);
1920 m_privileges.insert(priv);
1921 infostream<<priv<<" ";
1923 infostream<<std::endl;
1925 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1927 std::string datastring((char*)&data[2], datasize-2);
1928 std::istringstream is(datastring, std::ios_base::binary);
1930 // Store formspec in LocalPlayer
1931 Player *player = m_env.getLocalPlayer();
1932 assert(player != NULL);
1933 player->inventory_formspec = deSerializeLongString(is);
1935 else if(command == TOCLIENT_DETACHED_INVENTORY)
1937 std::string datastring((char*)&data[2], datasize-2);
1938 std::istringstream is(datastring, std::ios_base::binary);
1940 std::string name = deSerializeString(is);
1942 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1944 Inventory *inv = NULL;
1945 if(m_detached_inventories.count(name) > 0)
1946 inv = m_detached_inventories[name];
1948 inv = new Inventory(m_itemdef);
1949 m_detached_inventories[name] = inv;
1951 inv->deSerialize(is);
1953 else if(command == TOCLIENT_SHOW_FORMSPEC)
1955 std::string datastring((char*)&data[2], datasize-2);
1956 std::istringstream is(datastring, std::ios_base::binary);
1958 std::string formspec = deSerializeLongString(is);
1959 std::string formname = deSerializeString(is);
1962 event.type = CE_SHOW_FORMSPEC;
1963 // pointer is required as event is a struct only!
1964 // adding a std:string to a struct isn't possible
1965 event.show_formspec.formspec = new std::string(formspec);
1966 event.show_formspec.formname = new std::string(formname);
1967 m_client_event_queue.push_back(event);
1969 else if(command == TOCLIENT_SPAWN_PARTICLE)
1971 std::string datastring((char*)&data[2], datasize-2);
1972 std::istringstream is(datastring, std::ios_base::binary);
1974 v3f pos = readV3F1000(is);
1975 v3f vel = readV3F1000(is);
1976 v3f acc = readV3F1000(is);
1977 float expirationtime = readF1000(is);
1978 float size = readF1000(is);
1979 bool collisiondetection = readU8(is);
1980 std::string texture = deSerializeLongString(is);
1983 event.type = CE_SPAWN_PARTICLE;
1984 event.spawn_particle.pos = new v3f (pos);
1985 event.spawn_particle.vel = new v3f (vel);
1986 event.spawn_particle.acc = new v3f (acc);
1988 event.spawn_particle.expirationtime = expirationtime;
1989 event.spawn_particle.size = size;
1990 event.add_particlespawner.collisiondetection =
1992 event.spawn_particle.texture = new std::string(texture);
1994 m_client_event_queue.push_back(event);
1996 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1998 std::string datastring((char*)&data[2], datasize-2);
1999 std::istringstream is(datastring, std::ios_base::binary);
2001 u16 amount = readU16(is);
2002 float spawntime = readF1000(is);
2003 v3f minpos = readV3F1000(is);
2004 v3f maxpos = readV3F1000(is);
2005 v3f minvel = readV3F1000(is);
2006 v3f maxvel = readV3F1000(is);
2007 v3f minacc = readV3F1000(is);
2008 v3f maxacc = readV3F1000(is);
2009 float minexptime = readF1000(is);
2010 float maxexptime = readF1000(is);
2011 float minsize = readF1000(is);
2012 float maxsize = readF1000(is);
2013 bool collisiondetection = readU8(is);
2014 std::string texture = deSerializeLongString(is);
2015 u32 id = readU32(is);
2018 event.type = CE_ADD_PARTICLESPAWNER;
2019 event.add_particlespawner.amount = amount;
2020 event.add_particlespawner.spawntime = spawntime;
2022 event.add_particlespawner.minpos = new v3f (minpos);
2023 event.add_particlespawner.maxpos = new v3f (maxpos);
2024 event.add_particlespawner.minvel = new v3f (minvel);
2025 event.add_particlespawner.maxvel = new v3f (maxvel);
2026 event.add_particlespawner.minacc = new v3f (minacc);
2027 event.add_particlespawner.maxacc = new v3f (maxacc);
2029 event.add_particlespawner.minexptime = minexptime;
2030 event.add_particlespawner.maxexptime = maxexptime;
2031 event.add_particlespawner.minsize = minsize;
2032 event.add_particlespawner.maxsize = maxsize;
2033 event.add_particlespawner.collisiondetection = collisiondetection;
2034 event.add_particlespawner.texture = new std::string(texture);
2035 event.add_particlespawner.id = id;
2037 m_client_event_queue.push_back(event);
2039 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2041 std::string datastring((char*)&data[2], datasize-2);
2042 std::istringstream is(datastring, std::ios_base::binary);
2044 u32 id = readU16(is);
2047 event.type = CE_DELETE_PARTICLESPAWNER;
2048 event.delete_particlespawner.id = id;
2050 m_client_event_queue.push_back(event);
2052 else if(command == TOCLIENT_HUDADD)
2054 std::string datastring((char *)&data[2], datasize - 2);
2055 std::istringstream is(datastring, std::ios_base::binary);
2057 u32 id = readU32(is);
2058 u8 type = readU8(is);
2059 v2f pos = readV2F1000(is);
2060 std::string name = deSerializeString(is);
2061 v2f scale = readV2F1000(is);
2062 std::string text = deSerializeString(is);
2063 u32 number = readU32(is);
2064 u32 item = readU32(is);
2065 u32 dir = readU32(is);
2066 v2f align = readV2F1000(is);
2067 v2f offset = readV2F1000(is);
2070 event.type = CE_HUDADD;
2071 event.hudadd.id = id;
2072 event.hudadd.type = type;
2073 event.hudadd.pos = new v2f(pos);
2074 event.hudadd.name = new std::string(name);
2075 event.hudadd.scale = new v2f(scale);
2076 event.hudadd.text = new std::string(text);
2077 event.hudadd.number = number;
2078 event.hudadd.item = item;
2079 event.hudadd.dir = dir;
2080 event.hudadd.align = new v2f(align);
2081 event.hudadd.offset = new v2f(offset);
2082 m_client_event_queue.push_back(event);
2084 else if(command == TOCLIENT_HUDRM)
2086 std::string datastring((char *)&data[2], datasize - 2);
2087 std::istringstream is(datastring, std::ios_base::binary);
2089 u32 id = readU32(is);
2092 event.type = CE_HUDRM;
2093 event.hudrm.id = id;
2094 m_client_event_queue.push_back(event);
2096 else if(command == TOCLIENT_HUDCHANGE)
2102 std::string datastring((char *)&data[2], datasize - 2);
2103 std::istringstream is(datastring, std::ios_base::binary);
2105 u32 id = readU32(is);
2106 u8 stat = (HudElementStat)readU8(is);
2108 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2109 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2110 v2fdata = readV2F1000(is);
2111 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2112 sdata = deSerializeString(is);
2114 intdata = readU32(is);
2117 event.type = CE_HUDCHANGE;
2118 event.hudchange.id = id;
2119 event.hudchange.stat = (HudElementStat)stat;
2120 event.hudchange.v2fdata = new v2f(v2fdata);
2121 event.hudchange.sdata = new std::string(sdata);
2122 event.hudchange.data = intdata;
2123 m_client_event_queue.push_back(event);
2125 else if(command == TOCLIENT_HUD_SET_FLAGS)
2127 std::string datastring((char *)&data[2], datasize - 2);
2128 std::istringstream is(datastring, std::ios_base::binary);
2130 Player *player = m_env.getLocalPlayer();
2131 assert(player != NULL);
2133 u32 flags = readU32(is);
2134 u32 mask = readU32(is);
2136 player->hud_flags &= ~mask;
2137 player->hud_flags |= flags;
2141 infostream<<"Client: Ignoring unknown command "
2142 <<command<<std::endl;
2146 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2148 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2149 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2152 void Client::interact(u8 action, const PointedThing& pointed)
2154 if(connectedAndInitialized() == false){
2155 infostream<<"Client::interact() "
2156 "cancelled (not connected)"
2161 std::ostringstream os(std::ios_base::binary);
2167 [5] u32 length of the next item
2168 [9] serialized PointedThing
2170 0: start digging (from undersurface) or use
2171 1: stop digging (all parameters ignored)
2172 2: digging completed
2173 3: place block or item (to abovesurface)
2176 writeU16(os, TOSERVER_INTERACT);
2177 writeU8(os, action);
2178 writeU16(os, getPlayerItem());
2179 std::ostringstream tmp_os(std::ios::binary);
2180 pointed.serialize(tmp_os);
2181 os<<serializeLongString(tmp_os.str());
2183 std::string s = os.str();
2184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2187 Send(0, data, true);
2190 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2191 const std::map<std::string, std::string> &fields)
2193 std::ostringstream os(std::ios_base::binary);
2195 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2197 os<<serializeString(formname);
2198 writeU16(os, fields.size());
2199 for(std::map<std::string, std::string>::const_iterator
2200 i = fields.begin(); i != fields.end(); i++){
2201 const std::string &name = i->first;
2202 const std::string &value = i->second;
2203 os<<serializeString(name);
2204 os<<serializeLongString(value);
2208 std::string s = os.str();
2209 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2211 Send(0, data, true);
2214 void Client::sendInventoryFields(const std::string &formname,
2215 const std::map<std::string, std::string> &fields)
2217 std::ostringstream os(std::ios_base::binary);
2219 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2220 os<<serializeString(formname);
2221 writeU16(os, fields.size());
2222 for(std::map<std::string, std::string>::const_iterator
2223 i = fields.begin(); i != fields.end(); i++){
2224 const std::string &name = i->first;
2225 const std::string &value = i->second;
2226 os<<serializeString(name);
2227 os<<serializeLongString(value);
2231 std::string s = os.str();
2232 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2234 Send(0, data, true);
2237 void Client::sendInventoryAction(InventoryAction *a)
2239 std::ostringstream os(std::ios_base::binary);
2243 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2244 os.write((char*)buf, 2);
2249 std::string s = os.str();
2250 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2252 Send(0, data, true);
2255 void Client::sendChatMessage(const std::wstring &message)
2257 std::ostringstream os(std::ios_base::binary);
2261 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2262 os.write((char*)buf, 2);
2265 writeU16(buf, message.size());
2266 os.write((char*)buf, 2);
2269 for(u32 i=0; i<message.size(); i++)
2273 os.write((char*)buf, 2);
2277 std::string s = os.str();
2278 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2280 Send(0, data, true);
2283 void Client::sendChangePassword(const std::wstring oldpassword,
2284 const std::wstring newpassword)
2286 Player *player = m_env.getLocalPlayer();
2290 std::string playername = player->getName();
2291 std::string oldpwd = translatePassword(playername, oldpassword);
2292 std::string newpwd = translatePassword(playername, newpassword);
2294 std::ostringstream os(std::ios_base::binary);
2295 u8 buf[2+PASSWORD_SIZE*2];
2297 [0] u16 TOSERVER_PASSWORD
2298 [2] u8[28] old password
2299 [30] u8[28] new password
2302 writeU16(buf, TOSERVER_PASSWORD);
2303 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2305 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2306 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2308 buf[2+PASSWORD_SIZE-1] = 0;
2309 buf[30+PASSWORD_SIZE-1] = 0;
2310 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2313 std::string s = os.str();
2314 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2316 Send(0, data, true);
2320 void Client::sendDamage(u8 damage)
2322 DSTACK(__FUNCTION_NAME);
2323 std::ostringstream os(std::ios_base::binary);
2325 writeU16(os, TOSERVER_DAMAGE);
2326 writeU8(os, damage);
2329 std::string s = os.str();
2330 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2332 Send(0, data, true);
2335 void Client::sendRespawn()
2337 DSTACK(__FUNCTION_NAME);
2338 std::ostringstream os(std::ios_base::binary);
2340 writeU16(os, TOSERVER_RESPAWN);
2343 std::string s = os.str();
2344 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2346 Send(0, data, true);
2349 void Client::sendPlayerPos()
2351 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2353 LocalPlayer *myplayer = m_env.getLocalPlayer();
2354 if(myplayer == NULL)
2357 // Save bandwidth by only updating position when something changed
2358 if(myplayer->last_position == myplayer->getPosition() &&
2359 myplayer->last_speed == myplayer->getSpeed() &&
2360 myplayer->last_pitch == myplayer->getPitch() &&
2361 myplayer->last_yaw == myplayer->getYaw() &&
2362 myplayer->last_keyPressed == myplayer->keyPressed)
2365 myplayer->last_position = myplayer->getPosition();
2366 myplayer->last_speed = myplayer->getSpeed();
2367 myplayer->last_pitch = myplayer->getPitch();
2368 myplayer->last_yaw = myplayer->getYaw();
2369 myplayer->last_keyPressed = myplayer->keyPressed;
2373 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2374 our_peer_id = m_con.GetPeerID();
2377 // Set peer id if not set already
2378 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2379 myplayer->peer_id = our_peer_id;
2380 // Check that an existing peer_id is the same as the connection's
2381 assert(myplayer->peer_id == our_peer_id);
2383 v3f pf = myplayer->getPosition();
2384 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2385 v3f sf = myplayer->getSpeed();
2386 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2387 s32 pitch = myplayer->getPitch() * 100;
2388 s32 yaw = myplayer->getYaw() * 100;
2389 u32 keyPressed=myplayer->keyPressed;
2393 [2] v3s32 position*100
2394 [2+12] v3s32 speed*100
2395 [2+12+12] s32 pitch*100
2396 [2+12+12+4] s32 yaw*100
2397 [2+12+12+4+4] u32 keyPressed
2399 SharedBuffer<u8> data(2+12+12+4+4+4);
2400 writeU16(&data[0], TOSERVER_PLAYERPOS);
2401 writeV3S32(&data[2], position);
2402 writeV3S32(&data[2+12], speed);
2403 writeS32(&data[2+12+12], pitch);
2404 writeS32(&data[2+12+12+4], yaw);
2405 writeU32(&data[2+12+12+4+4], keyPressed);
2406 // Send as unreliable
2407 Send(0, data, false);
2410 void Client::sendPlayerItem(u16 item)
2412 Player *myplayer = m_env.getLocalPlayer();
2413 if(myplayer == NULL)
2416 u16 our_peer_id = m_con.GetPeerID();
2418 // Set peer id if not set already
2419 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2420 myplayer->peer_id = our_peer_id;
2421 // Check that an existing peer_id is the same as the connection's
2422 assert(myplayer->peer_id == our_peer_id);
2424 SharedBuffer<u8> data(2+2);
2425 writeU16(&data[0], TOSERVER_PLAYERITEM);
2426 writeU16(&data[2], item);
2429 Send(0, data, true);
2432 void Client::removeNode(v3s16 p)
2434 std::map<v3s16, MapBlock*> modified_blocks;
2438 //TimeTaker t("removeNodeAndUpdate", m_device);
2439 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2441 catch(InvalidPositionException &e)
2445 // add urgent task to update the modified node
2446 addUpdateMeshTaskForNode(p, false, true);
2448 for(std::map<v3s16, MapBlock * >::iterator
2449 i = modified_blocks.begin();
2450 i != modified_blocks.end(); ++i)
2452 addUpdateMeshTaskWithEdge(i->first);
2456 void Client::addNode(v3s16 p, MapNode n)
2458 TimeTaker timer1("Client::addNode()");
2460 std::map<v3s16, MapBlock*> modified_blocks;
2464 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2465 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2467 catch(InvalidPositionException &e)
2470 for(std::map<v3s16, MapBlock * >::iterator
2471 i = modified_blocks.begin();
2472 i != modified_blocks.end(); ++i)
2474 addUpdateMeshTaskWithEdge(i->first);
2478 void Client::setPlayerControl(PlayerControl &control)
2480 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2481 LocalPlayer *player = m_env.getLocalPlayer();
2482 assert(player != NULL);
2483 player->control = control;
2486 void Client::selectPlayerItem(u16 item)
2488 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2489 m_playeritem = item;
2490 m_inventory_updated = true;
2491 sendPlayerItem(item);
2494 // Returns true if the inventory of the local player has been
2495 // updated from the server. If it is true, it is set to false.
2496 bool Client::getLocalInventoryUpdated()
2498 // m_inventory_updated is behind envlock
2499 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2500 bool updated = m_inventory_updated;
2501 m_inventory_updated = false;
2505 // Copies the inventory of the local player to parameter
2506 void Client::getLocalInventory(Inventory &dst)
2508 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2509 Player *player = m_env.getLocalPlayer();
2510 assert(player != NULL);
2511 dst = player->inventory;
2514 Inventory* Client::getInventory(const InventoryLocation &loc)
2517 case InventoryLocation::UNDEFINED:
2520 case InventoryLocation::CURRENT_PLAYER:
2522 Player *player = m_env.getLocalPlayer();
2523 assert(player != NULL);
2524 return &player->inventory;
2527 case InventoryLocation::PLAYER:
2529 Player *player = m_env.getPlayer(loc.name.c_str());
2532 return &player->inventory;
2535 case InventoryLocation::NODEMETA:
2537 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2540 return meta->getInventory();
2543 case InventoryLocation::DETACHED:
2545 if(m_detached_inventories.count(loc.name) == 0)
2547 return m_detached_inventories[loc.name];
2555 void Client::inventoryAction(InventoryAction *a)
2558 Send it to the server
2560 sendInventoryAction(a);
2563 Predict some local inventory changes
2565 a->clientApply(this, this);
2571 ClientActiveObject * Client::getSelectedActiveObject(
2573 v3f from_pos_f_on_map,
2574 core::line3d<f32> shootline_on_map
2577 std::vector<DistanceSortedActiveObject> objects;
2579 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2581 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2584 // After this, the closest object is the first in the array.
2585 std::sort(objects.begin(), objects.end());
2587 for(u32 i=0; i<objects.size(); i++)
2589 ClientActiveObject *obj = objects[i].obj;
2591 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2592 if(selection_box == NULL)
2595 v3f pos = obj->getPosition();
2597 core::aabbox3d<f32> offsetted_box(
2598 selection_box->MinEdge + pos,
2599 selection_box->MaxEdge + pos
2602 if(offsetted_box.intersectsWithLine(shootline_on_map))
2604 //infostream<<"Returning selected object"<<std::endl;
2609 //infostream<<"No object selected; returning NULL."<<std::endl;
2613 void Client::printDebugInfo(std::ostream &os)
2615 //JMutexAutoLock lock1(m_fetchblock_mutex);
2616 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2618 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2619 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2620 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2624 std::list<std::string> Client::getConnectedPlayerNames()
2626 return m_env.getPlayerNames();
2629 float Client::getAnimationTime()
2631 return m_animation_time;
2634 int Client::getCrackLevel()
2636 return m_crack_level;
2639 void Client::setCrack(int level, v3s16 pos)
2641 int old_crack_level = m_crack_level;
2642 v3s16 old_crack_pos = m_crack_pos;
2644 m_crack_level = level;
2647 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2650 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2652 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2655 addUpdateMeshTaskForNode(pos, false, true);
2661 Player *player = m_env.getLocalPlayer();
2662 assert(player != NULL);
2666 bool Client::getChatMessage(std::wstring &message)
2668 if(m_chat_queue.size() == 0)
2670 message = m_chat_queue.pop_front();
2674 void Client::typeChatMessage(const std::wstring &message)
2676 // Discard empty line
2681 sendChatMessage(message);
2684 if (message[0] == L'/')
2686 m_chat_queue.push_back(
2687 (std::wstring)L"issued command: "+message);
2691 LocalPlayer *player = m_env.getLocalPlayer();
2692 assert(player != NULL);
2693 std::wstring name = narrow_to_wide(player->getName());
2694 m_chat_queue.push_back(
2695 (std::wstring)L"<"+name+L"> "+message);
2699 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2701 /*infostream<<"Client::addUpdateMeshTask(): "
2702 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2703 <<" ack_to_server="<<ack_to_server
2704 <<" urgent="<<urgent
2707 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2712 Create a task to update the mesh of the block
2715 MeshMakeData *data = new MeshMakeData(this);
2718 //TimeTaker timer("data fill");
2720 // Debug: 1-6ms, avg=2ms
2722 data->setCrack(m_crack_level, m_crack_pos);
2723 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2727 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2729 // Add task to queue
2730 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2732 /*infostream<<"Mesh update input queue size is "
2733 <<m_mesh_update_thread.m_queue_in.size()
2737 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2741 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2742 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2747 v3s16 p = blockpos + v3s16(0,0,0);
2748 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2749 addUpdateMeshTask(p, ack_to_server, urgent);
2751 catch(InvalidPositionException &e){}
2753 for (int i=0;i<6;i++)
2756 v3s16 p = blockpos + g_6dirs[i];
2757 addUpdateMeshTask(p, false, urgent);
2759 catch(InvalidPositionException &e){}
2763 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2767 infostream<<"Client::addUpdateMeshTaskForNode(): "
2768 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2772 v3s16 blockpos = getNodeBlockPos(nodepos);
2773 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2776 v3s16 p = blockpos + v3s16(0,0,0);
2777 addUpdateMeshTask(p, ack_to_server, urgent);
2779 catch(InvalidPositionException &e){}
2781 if(nodepos.X == blockpos_relative.X){
2783 v3s16 p = blockpos + v3s16(-1,0,0);
2784 addUpdateMeshTask(p, false, urgent);
2786 catch(InvalidPositionException &e){}
2788 if(nodepos.Y == blockpos_relative.Y){
2790 v3s16 p = blockpos + v3s16(0,-1,0);
2791 addUpdateMeshTask(p, false, urgent);
2793 catch(InvalidPositionException &e){}
2795 if(nodepos.Z == blockpos_relative.Z){
2797 v3s16 p = blockpos + v3s16(0,0,-1);
2798 addUpdateMeshTask(p, false, urgent);
2800 catch(InvalidPositionException &e){}
2804 ClientEvent Client::getClientEvent()
2806 if(m_client_event_queue.size() == 0)
2809 event.type = CE_NONE;
2812 return m_client_event_queue.pop_front();
2815 void draw_load_screen(const std::wstring &text,
2816 IrrlichtDevice* device, gui::IGUIFont* font,
2817 float dtime=0 ,int percent=0, bool clouds=true);
2818 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2820 infostream<<"Client::afterContentReceived() started"<<std::endl;
2821 assert(m_itemdef_received);
2822 assert(m_nodedef_received);
2823 assert(texturesReceived());
2825 // remove the information about which checksum each texture
2827 m_media_name_sha1_map.clear();
2829 // Rebuild inherited images and recreate textures
2830 infostream<<"- Rebuilding images and textures"<<std::endl;
2831 m_tsrc->rebuildImagesAndTextures();
2833 // Update texture atlas
2834 infostream<<"- Updating texture atlas"<<std::endl;
2835 if(g_settings->getBool("enable_texture_atlas"))
2836 m_tsrc->buildMainAtlas(this);
2839 m_shsrc->rebuildShaders();
2841 // Update node aliases
2842 infostream<<"- Updating node aliases"<<std::endl;
2843 m_nodedef->updateAliases(m_itemdef);
2845 // Update node textures
2846 infostream<<"- Updating node textures"<<std::endl;
2847 m_nodedef->updateTextures(m_tsrc);
2849 // Preload item textures and meshes if configured to
2850 if(g_settings->getBool("preload_item_visuals"))
2852 verbosestream<<"Updating item textures and meshes"<<std::endl;
2853 wchar_t* text = wgettext("Item textures...");
2854 draw_load_screen(text,device,font,0,0);
2855 std::set<std::string> names = m_itemdef->getAll();
2856 size_t size = names.size();
2859 for(std::set<std::string>::const_iterator
2860 i = names.begin(); i != names.end(); ++i){
2861 // Asking for these caches the result
2862 m_itemdef->getInventoryTexture(*i, this);
2863 m_itemdef->getWieldMesh(*i, this);
2865 percent = count*100/size;
2866 if (count%50 == 0) // only update every 50 item
2867 draw_load_screen(text,device,font,0,percent);
2872 // Start mesh update thread after setting up content definitions
2873 infostream<<"- Starting mesh update thread"<<std::endl;
2874 m_mesh_update_thread.Start();
2876 infostream<<"Client::afterContentReceived() done"<<std::endl;
2879 float Client::getRTT(void)
2882 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2883 } catch(con::PeerNotFoundException &e){
2888 // IGameDef interface
2890 IItemDefManager* Client::getItemDefManager()
2894 INodeDefManager* Client::getNodeDefManager()
2898 ICraftDefManager* Client::getCraftDefManager()
2901 //return m_craftdef;
2903 ITextureSource* Client::getTextureSource()
2907 IShaderSource* Client::getShaderSource()
2911 u16 Client::allocateUnknownNodeId(const std::string &name)
2913 errorstream<<"Client::allocateUnknownNodeId(): "
2914 <<"Client cannot allocate node IDs"<<std::endl;
2916 return CONTENT_IGNORE;
2918 ISoundManager* Client::getSoundManager()
2922 MtEventManager* Client::getEventManager()