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"
33 #include "nodemetadata.h"
37 #include <IFileSystem.h>
40 #include "clientmap.h"
41 #include "filecache.h"
43 #include "util/string.h"
45 #include "IMeshCache.h"
46 #include "util/serialize.h"
48 #include "util/directiontables.h"
51 #include <curl/curl.h>
54 static std::string getMediaCacheDir()
56 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
63 QueuedMeshUpdate::QueuedMeshUpdate():
66 ack_block_to_server(false)
70 QueuedMeshUpdate::~QueuedMeshUpdate()
80 MeshUpdateQueue::MeshUpdateQueue()
85 MeshUpdateQueue::~MeshUpdateQueue()
87 JMutexAutoLock lock(m_mutex);
89 for(std::vector<QueuedMeshUpdate*>::iterator
91 i != m_queue.end(); i++)
93 QueuedMeshUpdate *q = *i;
99 peer_id=0 adds with nobody to send to
101 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
103 DSTACK(__FUNCTION_NAME);
107 JMutexAutoLock lock(m_mutex);
113 Find if block is already in queue.
114 If it is, update the data and quit.
116 for(std::vector<QueuedMeshUpdate*>::iterator
118 i != m_queue.end(); i++)
120 QueuedMeshUpdate *q = *i;
126 if(ack_block_to_server)
127 q->ack_block_to_server = true;
135 QueuedMeshUpdate *q = new QueuedMeshUpdate;
138 q->ack_block_to_server = ack_block_to_server;
139 m_queue.push_back(q);
142 // Returned pointer must be deleted
143 // Returns NULL if queue is empty
144 QueuedMeshUpdate * MeshUpdateQueue::pop()
146 JMutexAutoLock lock(m_mutex);
148 bool must_be_urgent = !m_urgents.empty();
149 for(std::vector<QueuedMeshUpdate*>::iterator
151 i != m_queue.end(); i++)
153 QueuedMeshUpdate *q = *i;
154 if(must_be_urgent && m_urgents.count(q->p) == 0)
157 m_urgents.erase(q->p);
167 void * MeshUpdateThread::Thread()
171 log_register_thread("MeshUpdateThread");
173 DSTACK(__FUNCTION_NAME);
175 BEGIN_DEBUG_EXCEPTION_HANDLER
179 /*// Wait for output queue to flush.
180 // Allow 2 in queue, this makes less frametime jitter.
181 // Umm actually, there is no much difference
182 if(m_queue_out.size() >= 2)
188 QueuedMeshUpdate *q = m_queue_in.pop();
195 ScopeProfiler sp(g_profiler, "Client: Mesh making");
197 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
198 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
207 r.ack_block_to_server = q->ack_block_to_server;
209 /*infostream<<"MeshUpdateThread: Processed "
210 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
213 m_queue_out.push_back(r);
218 END_DEBUG_EXCEPTION_HANDLER(errorstream)
223 void * MediaFetchThread::Thread()
227 log_register_thread("MediaFetchThread");
229 DSTACK(__FUNCTION_NAME);
231 BEGIN_DEBUG_EXCEPTION_HANDLER
236 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
237 i != m_file_requests.end(); ++i) {
238 curl = curl_easy_init();
240 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
241 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
242 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
243 std::ostringstream stream;
244 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
245 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
246 res = curl_easy_perform(curl);
247 if (res == CURLE_OK) {
248 std::string data = stream.str();
249 m_file_data.push_back(make_pair(i->name, data));
251 m_failed.push_back(*i);
252 infostream << "cURL request failed for " << i->name << std::endl;
254 curl_easy_cleanup(curl);
258 END_DEBUG_EXCEPTION_HANDLER(errorstream)
264 IrrlichtDevice *device,
265 const char *playername,
266 std::string password,
267 MapDrawControl &control,
268 IWritableTextureSource *tsrc,
269 IWritableShaderSource *shsrc,
270 IWritableItemDefManager *itemdef,
271 IWritableNodeDefManager *nodedef,
272 ISoundManager *sound,
273 MtEventManager *event
281 m_mesh_update_thread(this),
283 new ClientMap(this, this, control,
284 device->getSceneManager()->getRootSceneNode(),
285 device->getSceneManager(), 666),
286 device->getSceneManager(),
289 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
291 m_server_ser_ver(SER_FMT_VER_INVALID),
293 m_inventory_updated(false),
294 m_inventory_from_server(NULL),
295 m_inventory_from_server_age(0.0),
300 m_password(password),
301 m_access_denied(false),
302 m_media_cache(getMediaCacheDir()),
303 m_media_receive_started(false),
305 m_media_received_count(0),
306 m_itemdef_received(false),
307 m_nodedef_received(false),
308 m_time_of_day_set(false),
309 m_last_time_of_day_f(-1),
310 m_time_of_day_update_timer(0),
311 m_recommended_send_interval(0.1),
312 m_removed_sounds_check_timer(0)
314 m_packetcounter_timer = 0.0;
315 //m_delete_unused_sectors_timer = 0.0;
316 m_connection_reinit_timer = 0.0;
317 m_avg_rtt_timer = 0.0;
318 m_playerpos_send_timer = 0.0;
319 m_ignore_damage_timer = 0.0;
321 // Build main texture atlas, now that the GameDef exists (that is, us)
322 if(g_settings->getBool("enable_texture_atlas"))
323 m_tsrc->buildMainAtlas(this);
325 infostream<<"Not building texture atlas."<<std::endl;
331 Player *player = new LocalPlayer(this);
333 player->updateName(playername);
335 m_env.addPlayer(player);
338 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
339 m_media_fetch_threads.push_back(new MediaFetchThread(this));
345 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
349 m_mesh_update_thread.setRun(false);
350 while(m_mesh_update_thread.IsRunning())
353 delete m_inventory_from_server;
355 // Delete detached inventories
357 for(std::map<std::string, Inventory*>::iterator
358 i = m_detached_inventories.begin();
359 i != m_detached_inventories.end(); i++){
364 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
365 i != m_media_fetch_threads.end(); ++i)
368 // cleanup 3d model meshes on client shutdown
369 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
370 scene::IAnimatedMesh * mesh =
371 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
374 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
378 void Client::connect(Address address)
380 DSTACK(__FUNCTION_NAME);
381 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
382 m_con.SetTimeoutMs(0);
383 m_con.Connect(address);
386 bool Client::connectedAndInitialized()
388 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
390 if(m_con.Connected() == false)
393 if(m_server_ser_ver == SER_FMT_VER_INVALID)
399 void Client::step(float dtime)
401 DSTACK(__FUNCTION_NAME);
407 if(m_ignore_damage_timer > dtime)
408 m_ignore_damage_timer -= dtime;
410 m_ignore_damage_timer = 0.0;
412 m_animation_time += dtime;
413 if(m_animation_time > 60.0)
414 m_animation_time -= 60.0;
416 m_time_of_day_update_timer += dtime;
418 //infostream<<"Client steps "<<dtime<<std::endl;
421 //TimeTaker timer("ReceiveAll()", m_device);
427 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
429 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
430 m_con.RunTimeouts(dtime);
437 float &counter = m_packetcounter_timer;
443 infostream<<"Client packetcounter (20s):"<<std::endl;
444 m_packetcounter.print(infostream);
445 m_packetcounter.clear();
449 // Get connection status
450 bool connected = connectedAndInitialized();
455 Delete unused sectors
457 NOTE: This jams the game for a while because deleting sectors
461 float &counter = m_delete_unused_sectors_timer;
469 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
471 core::list<v3s16> deleted_blocks;
473 float delete_unused_sectors_timeout =
474 g_settings->getFloat("client_delete_unused_sectors_timeout");
476 // Delete sector blocks
477 /*u32 num = m_env.getMap().unloadUnusedData
478 (delete_unused_sectors_timeout,
479 true, &deleted_blocks);*/
481 // Delete whole sectors
482 m_env.getMap().unloadUnusedData
483 (delete_unused_sectors_timeout,
486 if(deleted_blocks.size() > 0)
488 /*infostream<<"Client: Deleted blocks of "<<num
489 <<" unused sectors"<<std::endl;*/
490 /*infostream<<"Client: Deleted "<<num
491 <<" unused sectors"<<std::endl;*/
497 // Env is locked so con can be locked.
498 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
500 core::list<v3s16>::Iterator i = deleted_blocks.begin();
501 core::list<v3s16> sendlist;
504 if(sendlist.size() == 255 || i == deleted_blocks.end())
506 if(sendlist.size() == 0)
515 u32 replysize = 2+1+6*sendlist.size();
516 SharedBuffer<u8> reply(replysize);
517 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
518 reply[2] = sendlist.size();
520 for(core::list<v3s16>::Iterator
521 j = sendlist.begin();
522 j != sendlist.end(); j++)
524 writeV3S16(&reply[2+1+6*k], *j);
527 m_con.Send(PEER_ID_SERVER, 1, reply, true);
529 if(i == deleted_blocks.end())
535 sendlist.push_back(*i);
543 if(connected == false)
545 float &counter = m_connection_reinit_timer;
551 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
553 Player *myplayer = m_env.getLocalPlayer();
554 assert(myplayer != NULL);
556 // Send TOSERVER_INIT
557 // [0] u16 TOSERVER_INIT
558 // [2] u8 SER_FMT_VER_HIGHEST
559 // [3] u8[20] player_name
560 // [23] u8[28] password (new in some version)
561 // [51] u16 minimum supported network protocol version (added sometime)
562 // [53] u16 maximum supported network protocol version (added later than the previous one)
563 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
564 writeU16(&data[0], TOSERVER_INIT);
565 writeU8(&data[2], SER_FMT_VER_HIGHEST);
567 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
568 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
570 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
573 memset((char*)&data[23], 0, PASSWORD_SIZE);
574 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
576 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
577 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
579 // Send as unreliable
580 Send(0, data, false);
583 // Not connected, return
588 Do stuff if connected
592 Run Map's timers and unload unused data
594 const float map_timer_and_unload_dtime = 5.25;
595 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
597 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
598 std::list<v3s16> deleted_blocks;
599 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
600 g_settings->getFloat("client_unload_unused_data_timeout"),
603 /*if(deleted_blocks.size() > 0)
604 infostream<<"Client: Unloaded "<<deleted_blocks.size()
605 <<" unused blocks"<<std::endl;*/
609 NOTE: This loop is intentionally iterated the way it is.
612 std::list<v3s16>::iterator i = deleted_blocks.begin();
613 std::list<v3s16> sendlist;
616 if(sendlist.size() == 255 || i == deleted_blocks.end())
618 if(sendlist.size() == 0)
627 u32 replysize = 2+1+6*sendlist.size();
628 SharedBuffer<u8> reply(replysize);
629 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
630 reply[2] = sendlist.size();
632 for(std::list<v3s16>::iterator
633 j = sendlist.begin();
634 j != sendlist.end(); ++j)
636 writeV3S16(&reply[2+1+6*k], *j);
639 m_con.Send(PEER_ID_SERVER, 1, reply, true);
641 if(i == deleted_blocks.end())
647 sendlist.push_back(*i);
657 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
659 // Control local player (0ms)
660 LocalPlayer *player = m_env.getLocalPlayer();
661 assert(player != NULL);
662 player->applyControl(dtime);
664 //TimeTaker envtimer("env step", m_device);
673 ClientEnvEvent event = m_env.getClientEvent();
674 if(event.type == CEE_NONE)
678 else if(event.type == CEE_PLAYER_DAMAGE)
680 if(m_ignore_damage_timer <= 0)
682 u8 damage = event.player_damage.amount;
684 if(event.player_damage.send_to_server)
687 // Add to ClientEvent queue
689 event.type = CE_PLAYER_DAMAGE;
690 event.player_damage.amount = damage;
691 m_client_event_queue.push_back(event);
701 float &counter = m_avg_rtt_timer;
706 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
707 // connectedAndInitialized() is true, peer exists.
708 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
709 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
714 Send player position to server
717 float &counter = m_playerpos_send_timer;
719 if(counter >= m_recommended_send_interval)
727 Replace updated meshes
730 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
732 //TimeTaker timer("** Processing mesh update result queue");
735 /*infostream<<"Mesh update result queue size is "
736 <<m_mesh_update_thread.m_queue_out.size()
739 int num_processed_meshes = 0;
740 while(!m_mesh_update_thread.m_queue_out.empty())
742 num_processed_meshes++;
743 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
744 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
747 //JMutexAutoLock lock(block->mesh_mutex);
749 // Delete the old mesh
750 if(block->mesh != NULL)
752 // TODO: Remove hardware buffers of meshbuffers of block->mesh
757 // Replace with the new mesh
758 block->mesh = r.mesh;
760 if(r.ack_block_to_server)
762 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
763 <<","<<r.p.Z<<")"<<std::endl;*/
774 u32 replysize = 2+1+6;
775 SharedBuffer<u8> reply(replysize);
776 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
778 writeV3S16(&reply[3], r.p);
780 m_con.Send(PEER_ID_SERVER, 1, reply, true);
783 if(num_processed_meshes > 0)
784 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
790 if (m_media_receive_started) {
791 bool all_stopped = true;
792 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
793 thread != m_media_fetch_threads.end(); ++thread) {
794 all_stopped &= !(*thread)->IsRunning();
795 while (!(*thread)->m_file_data.empty()) {
796 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
797 ++m_media_received_count;
799 bool success = loadMedia(out.second, out.first);
801 verbosestream<<"Client: Loaded received media: "
802 <<"\""<<out.first<<"\". Caching."<<std::endl;
804 infostream<<"Client: Failed to load received media: "
805 <<"\""<<out.first<<"\". Not caching."<<std::endl;
809 bool did = fs::CreateAllDirs(getMediaCacheDir());
811 errorstream<<"Could not create media cache directory"
816 std::map<std::string, std::string>::iterator n;
817 n = m_media_name_sha1_map.find(out.first);
818 if(n == m_media_name_sha1_map.end())
819 errorstream<<"The server sent a file that has not "
820 <<"been announced."<<std::endl;
822 m_media_cache.update_sha1(out.second);
827 std::list<MediaRequest> fetch_failed;
828 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
829 thread != m_media_fetch_threads.end(); ++thread) {
830 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
831 request != (*thread)->m_failed.end(); ++request)
832 fetch_failed.push_back(*request);
833 (*thread)->m_failed.clear();
835 if (fetch_failed.size() > 0) {
836 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
837 << "Requesting them the usual way." << std::endl;
838 request_media(fetch_failed);
844 If the server didn't update the inventory in a while, revert
845 the local inventory (so the player notices the lag problem
846 and knows something is wrong).
848 if(m_inventory_from_server)
850 float interval = 10.0;
851 float count_before = floor(m_inventory_from_server_age / interval);
853 m_inventory_from_server_age += dtime;
855 float count_after = floor(m_inventory_from_server_age / interval);
857 if(count_after != count_before)
859 // Do this every <interval> seconds after TOCLIENT_INVENTORY
860 // Reset the locally changed inventory to the authoritative inventory
861 Player *player = m_env.getLocalPlayer();
862 player->inventory = *m_inventory_from_server;
863 m_inventory_updated = true;
868 Update positions of sounds attached to objects
871 for(std::map<int, u16>::iterator
872 i = m_sounds_to_objects.begin();
873 i != m_sounds_to_objects.end(); i++)
875 int client_id = i->first;
876 u16 object_id = i->second;
877 ClientActiveObject *cao = m_env.getActiveObject(object_id);
880 v3f pos = cao->getPosition();
881 m_sound->updateSoundPosition(client_id, pos);
886 Handle removed remotely initiated sounds
888 m_removed_sounds_check_timer += dtime;
889 if(m_removed_sounds_check_timer >= 2.32)
891 m_removed_sounds_check_timer = 0;
892 // Find removed sounds and clear references to them
893 std::set<s32> removed_server_ids;
894 for(std::map<s32, int>::iterator
895 i = m_sounds_server_to_client.begin();
896 i != m_sounds_server_to_client.end();)
898 s32 server_id = i->first;
899 int client_id = i->second;
901 if(!m_sound->soundExists(client_id)){
902 m_sounds_server_to_client.erase(server_id);
903 m_sounds_client_to_server.erase(client_id);
904 m_sounds_to_objects.erase(client_id);
905 removed_server_ids.insert(server_id);
909 if(removed_server_ids.size() != 0)
911 std::ostringstream os(std::ios_base::binary);
912 writeU16(os, TOSERVER_REMOVED_SOUNDS);
913 writeU16(os, removed_server_ids.size());
914 for(std::set<s32>::iterator i = removed_server_ids.begin();
915 i != removed_server_ids.end(); i++)
917 std::string s = os.str();
918 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
925 bool Client::loadMedia(const std::string &data, const std::string &filename)
927 // Silly irrlicht's const-incorrectness
928 Buffer<char> data_rw(data.c_str(), data.size());
932 const char *image_ext[] = {
933 ".png", ".jpg", ".bmp", ".tga",
934 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
937 name = removeStringEnd(filename, image_ext);
940 verbosestream<<"Client: Attempting to load image "
941 <<"file \""<<filename<<"\""<<std::endl;
943 io::IFileSystem *irrfs = m_device->getFileSystem();
944 video::IVideoDriver *vdrv = m_device->getVideoDriver();
946 // Create an irrlicht memory file
947 io::IReadFile *rfile = irrfs->createMemoryReadFile(
948 *data_rw, data_rw.getSize(), "_tempreadfile");
951 video::IImage *img = vdrv->createImageFromFile(rfile);
953 errorstream<<"Client: Cannot create image from data of "
954 <<"file \""<<filename<<"\""<<std::endl;
959 m_tsrc->insertSourceImage(filename, img);
966 const char *sound_ext[] = {
967 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
968 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
971 name = removeStringEnd(filename, sound_ext);
974 verbosestream<<"Client: Attempting to load sound "
975 <<"file \""<<filename<<"\""<<std::endl;
976 m_sound->loadSoundData(name, data);
980 const char *model_ext[] = {
981 ".x", ".b3d", ".md2", ".obj",
984 name = removeStringEnd(filename, model_ext);
987 verbosestream<<"Client: Storing model into Irrlicht: "
988 <<"\""<<filename<<"\""<<std::endl;
989 scene::ISceneManager *smgr = m_device->getSceneManager();
991 //check if mesh was already cached
992 scene::IAnimatedMesh *mesh =
993 smgr->getMeshCache()->getMeshByName(filename.c_str());
996 errorstream << "Multiple models with name: " << filename.c_str() <<
997 " found replacing previous model!" << std::endl;
999 smgr->getMeshCache()->removeMesh(mesh);
1003 io::IFileSystem *irrfs = m_device->getFileSystem();
1004 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1005 *data_rw, data_rw.getSize(), filename.c_str());
1008 mesh = smgr->getMesh(rfile);
1009 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1014 errorstream<<"Client: Don't know how to load file \""
1015 <<filename<<"\""<<std::endl;
1019 // Virtual methods from con::PeerHandler
1020 void Client::peerAdded(con::Peer *peer)
1022 infostream<<"Client::peerAdded(): peer->id="
1023 <<peer->id<<std::endl;
1025 void Client::deletingPeer(con::Peer *peer, bool timeout)
1027 infostream<<"Client::deletingPeer(): "
1028 "Server Peer is getting deleted "
1029 <<"(timeout="<<timeout<<")"<<std::endl;
1034 u16 number of files requested
1040 void Client::request_media(const std::list<MediaRequest> &file_requests)
1042 std::ostringstream os(std::ios_base::binary);
1043 writeU16(os, TOSERVER_REQUEST_MEDIA);
1044 writeU16(os, file_requests.size());
1046 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1047 i != file_requests.end(); ++i) {
1048 os<<serializeString(i->name);
1052 std::string s = os.str();
1053 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1055 Send(0, data, true);
1056 infostream<<"Client: Sending media request list to server ("
1057 <<file_requests.size()<<" files)"<<std::endl;
1060 void Client::ReceiveAll()
1062 DSTACK(__FUNCTION_NAME);
1063 u32 start_ms = porting::getTimeMs();
1066 // Limit time even if there would be huge amounts of data to
1068 if(porting::getTimeMs() > start_ms + 100)
1073 g_profiler->graphAdd("client_received_packets", 1);
1075 catch(con::NoIncomingDataException &e)
1079 catch(con::InvalidIncomingDataException &e)
1081 infostream<<"Client::ReceiveAll(): "
1082 "InvalidIncomingDataException: what()="
1083 <<e.what()<<std::endl;
1088 void Client::Receive()
1090 DSTACK(__FUNCTION_NAME);
1091 SharedBuffer<u8> data;
1095 //TimeTaker t1("con mutex and receive", m_device);
1096 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1097 datasize = m_con.Receive(sender_peer_id, data);
1099 //TimeTaker t1("ProcessData", m_device);
1100 ProcessData(*data, datasize, sender_peer_id);
1104 sender_peer_id given to this shall be quaranteed to be a valid peer
1106 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1108 DSTACK(__FUNCTION_NAME);
1110 // Ignore packets that don't even fit a command
1113 m_packetcounter.add(60000);
1117 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1119 //infostream<<"Client: received command="<<command<<std::endl;
1120 m_packetcounter.add((u16)command);
1123 If this check is removed, be sure to change the queue
1124 system to know the ids
1126 if(sender_peer_id != PEER_ID_SERVER)
1128 infostream<<"Client::ProcessData(): Discarding data not "
1129 "coming from server: peer_id="<<sender_peer_id
1134 u8 ser_version = m_server_ser_ver;
1136 //infostream<<"Client received command="<<(int)command<<std::endl;
1138 if(command == TOCLIENT_INIT)
1143 u8 deployed = data[2];
1145 infostream<<"Client: TOCLIENT_INIT received with "
1146 "deployed="<<((int)deployed&0xff)<<std::endl;
1148 if(deployed < SER_FMT_VER_LOWEST
1149 || deployed > SER_FMT_VER_HIGHEST)
1151 infostream<<"Client: TOCLIENT_INIT: Server sent "
1152 <<"unsupported ser_fmt_ver"<<std::endl;
1156 m_server_ser_ver = deployed;
1158 // Get player position
1159 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1160 if(datasize >= 2+1+6)
1161 playerpos_s16 = readV3S16(&data[2+1]);
1162 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1165 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1167 // Set player position
1168 Player *player = m_env.getLocalPlayer();
1169 assert(player != NULL);
1170 player->setPosition(playerpos_f);
1173 if(datasize >= 2+1+6+8)
1176 m_map_seed = readU64(&data[2+1+6]);
1177 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1180 if(datasize >= 2+1+6+8+4)
1183 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1184 infostream<<"Client: received recommended send interval "
1185 <<m_recommended_send_interval<<std::endl;
1190 SharedBuffer<u8> reply(replysize);
1191 writeU16(&reply[0], TOSERVER_INIT2);
1193 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1198 if(command == TOCLIENT_ACCESS_DENIED)
1200 // The server didn't like our password. Note, this needs
1201 // to be processed even if the serialisation format has
1202 // not been agreed yet, the same as TOCLIENT_INIT.
1203 m_access_denied = true;
1204 m_access_denied_reason = L"Unknown";
1207 std::string datastring((char*)&data[2], datasize-2);
1208 std::istringstream is(datastring, std::ios_base::binary);
1209 m_access_denied_reason = deSerializeWideString(is);
1214 if(ser_version == SER_FMT_VER_INVALID)
1216 infostream<<"Client: Server serialization"
1217 " format invalid or not initialized."
1218 " Skipping incoming command="<<command<<std::endl;
1222 // Just here to avoid putting the two if's together when
1223 // making some copypasta
1226 if(command == TOCLIENT_REMOVENODE)
1231 p.X = readS16(&data[2]);
1232 p.Y = readS16(&data[4]);
1233 p.Z = readS16(&data[6]);
1235 //TimeTaker t1("TOCLIENT_REMOVENODE");
1239 else if(command == TOCLIENT_ADDNODE)
1241 if(datasize < 8 + MapNode::serializedLength(ser_version))
1245 p.X = readS16(&data[2]);
1246 p.Y = readS16(&data[4]);
1247 p.Z = readS16(&data[6]);
1249 //TimeTaker t1("TOCLIENT_ADDNODE");
1252 n.deSerialize(&data[8], ser_version);
1256 else if(command == TOCLIENT_BLOCKDATA)
1258 // Ignore too small packet
1263 p.X = readS16(&data[2]);
1264 p.Y = readS16(&data[4]);
1265 p.Z = readS16(&data[6]);
1267 /*infostream<<"Client: Thread: BLOCKDATA for ("
1268 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1269 /*infostream<<"Client: Thread: BLOCKDATA for ("
1270 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1272 std::string datastring((char*)&data[8], datasize-8);
1273 std::istringstream istr(datastring, std::ios_base::binary);
1278 v2s16 p2d(p.X, p.Z);
1279 sector = m_env.getMap().emergeSector(p2d);
1281 assert(sector->getPos() == p2d);
1283 //TimeTaker timer("MapBlock deSerialize");
1286 block = sector->getBlockNoCreateNoEx(p.Y);
1290 Update an existing block
1292 //infostream<<"Updating"<<std::endl;
1293 block->deSerialize(istr, ser_version, false);
1300 //infostream<<"Creating new"<<std::endl;
1301 block = new MapBlock(&m_env.getMap(), p, this);
1302 block->deSerialize(istr, ser_version, false);
1303 sector->insertBlock(block);
1317 u32 replysize = 2+1+6;
1318 SharedBuffer<u8> reply(replysize);
1319 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1321 writeV3S16(&reply[3], p);
1323 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1327 Add it to mesh update queue and set it to be acknowledged after update.
1329 //infostream<<"Adding mesh update task for received block"<<std::endl;
1330 addUpdateMeshTaskWithEdge(p, true);
1332 else if(command == TOCLIENT_INVENTORY)
1337 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1340 //TimeTaker t2("mutex locking", m_device);
1341 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1344 //TimeTaker t3("istringstream init", m_device);
1345 std::string datastring((char*)&data[2], datasize-2);
1346 std::istringstream is(datastring, std::ios_base::binary);
1349 //m_env.printPlayers(infostream);
1351 //TimeTaker t4("player get", m_device);
1352 Player *player = m_env.getLocalPlayer();
1353 assert(player != NULL);
1356 //TimeTaker t1("inventory.deSerialize()", m_device);
1357 player->inventory.deSerialize(is);
1360 m_inventory_updated = true;
1362 delete m_inventory_from_server;
1363 m_inventory_from_server = new Inventory(player->inventory);
1364 m_inventory_from_server_age = 0.0;
1366 //infostream<<"Client got player inventory:"<<std::endl;
1367 //player->inventory.print(infostream);
1370 else if(command == TOCLIENT_TIME_OF_DAY)
1375 u16 time_of_day = readU16(&data[2]);
1376 time_of_day = time_of_day % 24000;
1377 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1378 float time_speed = 0;
1379 if(datasize >= 2 + 2 + 4){
1380 time_speed = readF1000(&data[4]);
1382 // Old message; try to approximate speed of time by ourselves
1383 float time_of_day_f = (float)time_of_day / 24000.0;
1384 float tod_diff_f = 0;
1385 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1386 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1388 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1389 m_last_time_of_day_f = time_of_day_f;
1390 float time_diff = m_time_of_day_update_timer;
1391 m_time_of_day_update_timer = 0;
1392 if(m_time_of_day_set){
1393 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1394 infostream<<"Client: Measured time_of_day speed (old format): "
1395 <<time_speed<<" tod_diff_f="<<tod_diff_f
1396 <<" time_diff="<<time_diff<<std::endl;
1400 // Update environment
1401 m_env.setTimeOfDay(time_of_day);
1402 m_env.setTimeOfDaySpeed(time_speed);
1403 m_time_of_day_set = true;
1405 u32 dr = m_env.getDayNightRatio();
1406 verbosestream<<"Client: time_of_day="<<time_of_day
1407 <<" time_speed="<<time_speed
1408 <<" dr="<<dr<<std::endl;
1410 else if(command == TOCLIENT_CHAT_MESSAGE)
1418 std::string datastring((char*)&data[2], datasize-2);
1419 std::istringstream is(datastring, std::ios_base::binary);
1422 is.read((char*)buf, 2);
1423 u16 len = readU16(buf);
1425 std::wstring message;
1426 for(u16 i=0; i<len; i++)
1428 is.read((char*)buf, 2);
1429 message += (wchar_t)readU16(buf);
1432 /*infostream<<"Client received chat message: "
1433 <<wide_to_narrow(message)<<std::endl;*/
1435 m_chat_queue.push_back(message);
1437 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1439 //if(g_settings->getBool("enable_experimental"))
1443 u16 count of removed objects
1444 for all removed objects {
1447 u16 count of added objects
1448 for all added objects {
1451 u32 initialization data length
1452 string initialization data
1457 // Get all data except the command number
1458 std::string datastring((char*)&data[2], datasize-2);
1459 // Throw them in an istringstream
1460 std::istringstream is(datastring, std::ios_base::binary);
1464 // Read removed objects
1466 u16 removed_count = readU16((u8*)buf);
1467 for(u16 i=0; i<removed_count; i++)
1470 u16 id = readU16((u8*)buf);
1473 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1474 m_env.removeActiveObject(id);
1478 // Read added objects
1480 u16 added_count = readU16((u8*)buf);
1481 for(u16 i=0; i<added_count; i++)
1484 u16 id = readU16((u8*)buf);
1486 u8 type = readU8((u8*)buf);
1487 std::string data = deSerializeLongString(is);
1490 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1491 m_env.addActiveObject(id, type, data);
1496 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1498 //if(g_settings->getBool("enable_experimental"))
1510 // Get all data except the command number
1511 std::string datastring((char*)&data[2], datasize-2);
1512 // Throw them in an istringstream
1513 std::istringstream is(datastring, std::ios_base::binary);
1515 while(is.eof() == false)
1519 u16 id = readU16((u8*)buf);
1523 u16 message_size = readU16((u8*)buf);
1524 std::string message;
1525 message.reserve(message_size);
1526 for(u16 i=0; i<message_size; i++)
1529 message.append(buf, 1);
1531 // Pass on to the environment
1533 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1534 m_env.processActiveObjectMessage(id, message);
1539 else if(command == TOCLIENT_MOVEMENT)
1541 std::string datastring((char*)&data[2], datasize-2);
1542 std::istringstream is(datastring, std::ios_base::binary);
1543 Player *player = m_env.getLocalPlayer();
1544 assert(player != NULL);
1546 player->movement_acceleration_default = readF1000(is) * BS;
1547 player->movement_acceleration_air = readF1000(is) * BS;
1548 player->movement_acceleration_fast = readF1000(is) * BS;
1549 player->movement_speed_walk = readF1000(is) * BS;
1550 player->movement_speed_crouch = readF1000(is) * BS;
1551 player->movement_speed_fast = readF1000(is) * BS;
1552 player->movement_speed_climb = readF1000(is) * BS;
1553 player->movement_speed_jump = readF1000(is) * BS;
1554 player->movement_liquid_fluidity = readF1000(is) * BS;
1555 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1556 player->movement_liquid_sink = readF1000(is) * BS;
1557 player->movement_gravity = readF1000(is) * BS;
1559 else if(command == TOCLIENT_HP)
1561 std::string datastring((char*)&data[2], datasize-2);
1562 std::istringstream is(datastring, std::ios_base::binary);
1563 Player *player = m_env.getLocalPlayer();
1564 assert(player != NULL);
1565 u8 oldhp = player->hp;
1571 // Add to ClientEvent queue
1573 event.type = CE_PLAYER_DAMAGE;
1574 event.player_damage.amount = oldhp - hp;
1575 m_client_event_queue.push_back(event);
1578 else if(command == TOCLIENT_MOVE_PLAYER)
1580 std::string datastring((char*)&data[2], datasize-2);
1581 std::istringstream is(datastring, std::ios_base::binary);
1582 Player *player = m_env.getLocalPlayer();
1583 assert(player != NULL);
1584 v3f pos = readV3F1000(is);
1585 f32 pitch = readF1000(is);
1586 f32 yaw = readF1000(is);
1587 player->setPosition(pos);
1588 /*player->setPitch(pitch);
1589 player->setYaw(yaw);*/
1591 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1592 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1598 Add to ClientEvent queue.
1599 This has to be sent to the main program because otherwise
1600 it would just force the pitch and yaw values to whatever
1601 the camera points to.
1604 event.type = CE_PLAYER_FORCE_MOVE;
1605 event.player_force_move.pitch = pitch;
1606 event.player_force_move.yaw = yaw;
1607 m_client_event_queue.push_back(event);
1609 // Ignore damage for a few seconds, so that the player doesn't
1610 // get damage from falling on ground
1611 m_ignore_damage_timer = 3.0;
1613 else if(command == TOCLIENT_PLAYERITEM)
1615 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1617 else if(command == TOCLIENT_DEATHSCREEN)
1619 std::string datastring((char*)&data[2], datasize-2);
1620 std::istringstream is(datastring, std::ios_base::binary);
1622 bool set_camera_point_target = readU8(is);
1623 v3f camera_point_target = readV3F1000(is);
1626 event.type = CE_DEATHSCREEN;
1627 event.deathscreen.set_camera_point_target = set_camera_point_target;
1628 event.deathscreen.camera_point_target_x = camera_point_target.X;
1629 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1630 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1631 m_client_event_queue.push_back(event);
1633 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1635 std::string datastring((char*)&data[2], datasize-2);
1636 std::istringstream is(datastring, std::ios_base::binary);
1638 // Mesh update thread must be stopped while
1639 // updating content definitions
1640 assert(!m_mesh_update_thread.IsRunning());
1642 int num_files = readU16(is);
1644 infostream<<"Client: Received media announcement: packet size: "
1645 <<datasize<<std::endl;
1647 std::list<MediaRequest> file_requests;
1649 for(int i=0; i<num_files; i++)
1651 //read file from cache
1652 std::string name = deSerializeString(is);
1653 std::string sha1_base64 = deSerializeString(is);
1655 // if name contains illegal characters, ignore the file
1656 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1657 errorstream<<"Client: ignoring illegal file name "
1658 <<"sent by server: \""<<name<<"\""<<std::endl;
1662 std::string sha1_raw = base64_decode(sha1_base64);
1663 std::string sha1_hex = hex_encode(sha1_raw);
1664 std::ostringstream tmp_os(std::ios_base::binary);
1665 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1666 m_media_name_sha1_map[name] = sha1_raw;
1668 // If found in cache, try to load it from there
1671 bool success = loadMedia(tmp_os.str(), name);
1673 verbosestream<<"Client: Loaded cached media: "
1674 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1677 infostream<<"Client: Failed to load cached media: "
1678 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1681 // Didn't load from cache; queue it to be requested
1682 verbosestream<<"Client: Adding file to request list: \""
1683 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1684 file_requests.push_back(MediaRequest(name));
1687 std::string remote_media = "";
1689 remote_media = deSerializeString(is);
1691 catch(SerializationError) {
1692 // not supported by server or turned off
1695 m_media_count = file_requests.size();
1696 m_media_receive_started = true;
1698 if (remote_media == "" || !USE_CURL) {
1699 request_media(file_requests);
1702 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1703 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1704 i != file_requests.end(); ++i) {
1705 (*cur)->m_file_requests.push_back(*i);
1707 if (cur == m_media_fetch_threads.end())
1708 cur = m_media_fetch_threads.begin();
1710 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1711 i != m_media_fetch_threads.end(); ++i) {
1712 (*i)->m_remote_url = remote_media;
1717 // notify server we received everything
1718 std::ostringstream os(std::ios_base::binary);
1719 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1720 std::string s = os.str();
1721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1723 Send(0, data, true);
1726 event.type = CE_TEXTURES_UPDATED;
1727 m_client_event_queue.push_back(event);
1729 else if(command == TOCLIENT_MEDIA)
1731 if (m_media_count == 0)
1733 std::string datastring((char*)&data[2], datasize-2);
1734 std::istringstream is(datastring, std::ios_base::binary);
1736 // Mesh update thread must be stopped while
1737 // updating content definitions
1738 assert(!m_mesh_update_thread.IsRunning());
1742 u16 total number of file bunches
1743 u16 index of this bunch
1744 u32 number of files in this bunch
1752 int num_bunches = readU16(is);
1753 int bunch_i = readU16(is);
1754 int num_files = readU32(is);
1755 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1756 <<num_bunches<<" files="<<num_files
1757 <<" size="<<datasize<<std::endl;
1758 for(int i=0; i<num_files; i++){
1759 m_media_received_count++;
1760 std::string name = deSerializeString(is);
1761 std::string data = deSerializeLongString(is);
1763 // if name contains illegal characters, ignore the file
1764 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1765 errorstream<<"Client: ignoring illegal file name "
1766 <<"sent by server: \""<<name<<"\""<<std::endl;
1770 bool success = loadMedia(data, name);
1772 verbosestream<<"Client: Loaded received media: "
1773 <<"\""<<name<<"\". Caching."<<std::endl;
1775 infostream<<"Client: Failed to load received media: "
1776 <<"\""<<name<<"\". Not caching."<<std::endl;
1780 bool did = fs::CreateAllDirs(getMediaCacheDir());
1782 errorstream<<"Could not create media cache directory"
1787 std::map<std::string, std::string>::iterator n;
1788 n = m_media_name_sha1_map.find(name);
1789 if(n == m_media_name_sha1_map.end())
1790 errorstream<<"The server sent a file that has not "
1791 <<"been announced."<<std::endl;
1793 m_media_cache.update_sha1(data);
1798 event.type = CE_TEXTURES_UPDATED;
1799 m_client_event_queue.push_back(event);
1801 else if(command == TOCLIENT_TOOLDEF)
1803 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1805 else if(command == TOCLIENT_NODEDEF)
1807 infostream<<"Client: Received node definitions: packet size: "
1808 <<datasize<<std::endl;
1810 // Mesh update thread must be stopped while
1811 // updating content definitions
1812 assert(!m_mesh_update_thread.IsRunning());
1814 // Decompress node definitions
1815 std::string datastring((char*)&data[2], datasize-2);
1816 std::istringstream is(datastring, std::ios_base::binary);
1817 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1818 std::ostringstream tmp_os;
1819 decompressZlib(tmp_is, tmp_os);
1821 // Deserialize node definitions
1822 std::istringstream tmp_is2(tmp_os.str());
1823 m_nodedef->deSerialize(tmp_is2);
1824 m_nodedef_received = true;
1826 else if(command == TOCLIENT_CRAFTITEMDEF)
1828 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1830 else if(command == TOCLIENT_ITEMDEF)
1832 infostream<<"Client: Received item definitions: packet size: "
1833 <<datasize<<std::endl;
1835 // Mesh update thread must be stopped while
1836 // updating content definitions
1837 assert(!m_mesh_update_thread.IsRunning());
1839 // Decompress item definitions
1840 std::string datastring((char*)&data[2], datasize-2);
1841 std::istringstream is(datastring, std::ios_base::binary);
1842 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1843 std::ostringstream tmp_os;
1844 decompressZlib(tmp_is, tmp_os);
1846 // Deserialize node definitions
1847 std::istringstream tmp_is2(tmp_os.str());
1848 m_itemdef->deSerialize(tmp_is2);
1849 m_itemdef_received = true;
1851 else if(command == TOCLIENT_PLAY_SOUND)
1853 std::string datastring((char*)&data[2], datasize-2);
1854 std::istringstream is(datastring, std::ios_base::binary);
1856 s32 server_id = readS32(is);
1857 std::string name = deSerializeString(is);
1858 float gain = readF1000(is);
1859 int type = readU8(is); // 0=local, 1=positional, 2=object
1860 v3f pos = readV3F1000(is);
1861 u16 object_id = readU16(is);
1862 bool loop = readU8(is);
1867 client_id = m_sound->playSound(name, loop, gain);
1869 case 1: // positional
1870 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1873 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1875 pos = cao->getPosition();
1876 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1877 // TODO: Set up sound to move with object
1882 if(client_id != -1){
1883 m_sounds_server_to_client[server_id] = client_id;
1884 m_sounds_client_to_server[client_id] = server_id;
1886 m_sounds_to_objects[client_id] = object_id;
1889 else if(command == TOCLIENT_STOP_SOUND)
1891 std::string datastring((char*)&data[2], datasize-2);
1892 std::istringstream is(datastring, std::ios_base::binary);
1894 s32 server_id = readS32(is);
1895 std::map<s32, int>::iterator i =
1896 m_sounds_server_to_client.find(server_id);
1897 if(i != m_sounds_server_to_client.end()){
1898 int client_id = i->second;
1899 m_sound->stopSound(client_id);
1902 else if(command == TOCLIENT_PRIVILEGES)
1904 std::string datastring((char*)&data[2], datasize-2);
1905 std::istringstream is(datastring, std::ios_base::binary);
1907 m_privileges.clear();
1908 infostream<<"Client: Privileges updated: ";
1909 u16 num_privileges = readU16(is);
1910 for(u16 i=0; i<num_privileges; i++){
1911 std::string priv = deSerializeString(is);
1912 m_privileges.insert(priv);
1913 infostream<<priv<<" ";
1915 infostream<<std::endl;
1917 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1919 std::string datastring((char*)&data[2], datasize-2);
1920 std::istringstream is(datastring, std::ios_base::binary);
1922 // Store formspec in LocalPlayer
1923 Player *player = m_env.getLocalPlayer();
1924 assert(player != NULL);
1925 player->inventory_formspec = deSerializeLongString(is);
1927 else if(command == TOCLIENT_DETACHED_INVENTORY)
1929 std::string datastring((char*)&data[2], datasize-2);
1930 std::istringstream is(datastring, std::ios_base::binary);
1932 std::string name = deSerializeString(is);
1934 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1936 Inventory *inv = NULL;
1937 if(m_detached_inventories.count(name) > 0)
1938 inv = m_detached_inventories[name];
1940 inv = new Inventory(m_itemdef);
1941 m_detached_inventories[name] = inv;
1943 inv->deSerialize(is);
1945 else if(command == TOCLIENT_SHOW_FORMSPEC)
1947 std::string datastring((char*)&data[2], datasize-2);
1948 std::istringstream is(datastring, std::ios_base::binary);
1950 std::string formspec = deSerializeLongString(is);
1951 std::string formname = deSerializeString(is);
1954 event.type = CE_SHOW_FORMSPEC;
1955 // pointer is required as event is a struct only!
1956 // adding a std:string to a struct isn't possible
1957 event.show_formspec.formspec = new std::string(formspec);
1958 event.show_formspec.formname = new std::string(formname);
1959 m_client_event_queue.push_back(event);
1961 else if(command == TOCLIENT_SPAWN_PARTICLE)
1963 std::string datastring((char*)&data[2], datasize-2);
1964 std::istringstream is(datastring, std::ios_base::binary);
1966 v3f pos = readV3F1000(is);
1967 v3f vel = readV3F1000(is);
1968 v3f acc = readV3F1000(is);
1969 float expirationtime = readF1000(is);
1970 float size = readF1000(is);
1971 bool collisiondetection = readU8(is);
1972 std::string texture = deSerializeLongString(is);
1975 event.type = CE_SPAWN_PARTICLE;
1976 event.spawn_particle.pos = new v3f (pos);
1977 event.spawn_particle.vel = new v3f (vel);
1978 event.spawn_particle.acc = new v3f (acc);
1980 event.spawn_particle.expirationtime = expirationtime;
1981 event.spawn_particle.size = size;
1982 event.add_particlespawner.collisiondetection =
1984 event.spawn_particle.texture = new std::string(texture);
1986 m_client_event_queue.push_back(event);
1988 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1990 std::string datastring((char*)&data[2], datasize-2);
1991 std::istringstream is(datastring, std::ios_base::binary);
1993 u16 amount = readU16(is);
1994 float spawntime = readF1000(is);
1995 v3f minpos = readV3F1000(is);
1996 v3f maxpos = readV3F1000(is);
1997 v3f minvel = readV3F1000(is);
1998 v3f maxvel = readV3F1000(is);
1999 v3f minacc = readV3F1000(is);
2000 v3f maxacc = readV3F1000(is);
2001 float minexptime = readF1000(is);
2002 float maxexptime = readF1000(is);
2003 float minsize = readF1000(is);
2004 float maxsize = readF1000(is);
2005 bool collisiondetection = readU8(is);
2006 std::string texture = deSerializeLongString(is);
2007 u32 id = readU32(is);
2010 event.type = CE_ADD_PARTICLESPAWNER;
2011 event.add_particlespawner.amount = amount;
2012 event.add_particlespawner.spawntime = spawntime;
2014 event.add_particlespawner.minpos = new v3f (minpos);
2015 event.add_particlespawner.maxpos = new v3f (maxpos);
2016 event.add_particlespawner.minvel = new v3f (minvel);
2017 event.add_particlespawner.maxvel = new v3f (maxvel);
2018 event.add_particlespawner.minacc = new v3f (minacc);
2019 event.add_particlespawner.maxacc = new v3f (maxacc);
2021 event.add_particlespawner.minexptime = minexptime;
2022 event.add_particlespawner.maxexptime = maxexptime;
2023 event.add_particlespawner.minsize = minsize;
2024 event.add_particlespawner.maxsize = maxsize;
2025 event.add_particlespawner.collisiondetection = collisiondetection;
2026 event.add_particlespawner.texture = new std::string(texture);
2027 event.add_particlespawner.id = id;
2029 m_client_event_queue.push_back(event);
2031 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2033 std::string datastring((char*)&data[2], datasize-2);
2034 std::istringstream is(datastring, std::ios_base::binary);
2036 u32 id = readU16(is);
2039 event.type = CE_DELETE_PARTICLESPAWNER;
2040 event.delete_particlespawner.id = id;
2042 m_client_event_queue.push_back(event);
2044 else if(command == TOCLIENT_HUDADD)
2046 std::string datastring((char *)&data[2], datasize - 2);
2047 std::istringstream is(datastring, std::ios_base::binary);
2049 u32 id = readU32(is);
2050 u8 type = readU8(is);
2051 v2f pos = readV2F1000(is);
2052 std::string name = deSerializeString(is);
2053 v2f scale = readV2F1000(is);
2054 std::string text = deSerializeString(is);
2055 u32 number = readU32(is);
2056 u32 item = readU32(is);
2057 u32 dir = readU32(is);
2058 v2f align = readV2F1000(is);
2059 v2f offset = readV2F1000(is);
2062 event.type = CE_HUDADD;
2063 event.hudadd.id = id;
2064 event.hudadd.type = type;
2065 event.hudadd.pos = new v2f(pos);
2066 event.hudadd.name = new std::string(name);
2067 event.hudadd.scale = new v2f(scale);
2068 event.hudadd.text = new std::string(text);
2069 event.hudadd.number = number;
2070 event.hudadd.item = item;
2071 event.hudadd.dir = dir;
2072 event.hudadd.align = new v2f(align);
2073 event.hudadd.offset = new v2f(offset);
2074 m_client_event_queue.push_back(event);
2076 else if(command == TOCLIENT_HUDRM)
2078 std::string datastring((char *)&data[2], datasize - 2);
2079 std::istringstream is(datastring, std::ios_base::binary);
2081 u32 id = readU32(is);
2084 event.type = CE_HUDRM;
2085 event.hudrm.id = id;
2086 m_client_event_queue.push_back(event);
2088 else if(command == TOCLIENT_HUDCHANGE)
2094 std::string datastring((char *)&data[2], datasize - 2);
2095 std::istringstream is(datastring, std::ios_base::binary);
2097 u32 id = readU32(is);
2098 u8 stat = (HudElementStat)readU8(is);
2100 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE)
2101 v2fdata = readV2F1000(is);
2102 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2103 sdata = deSerializeString(is);
2105 intdata = readU32(is);
2108 event.type = CE_HUDCHANGE;
2109 event.hudchange.id = id;
2110 event.hudchange.stat = (HudElementStat)stat;
2111 event.hudchange.v2fdata = new v2f(v2fdata);
2112 event.hudchange.sdata = new std::string(sdata);
2113 event.hudchange.data = intdata;
2114 m_client_event_queue.push_back(event);
2118 infostream<<"Client: Ignoring unknown command "
2119 <<command<<std::endl;
2123 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2125 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2126 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2129 void Client::interact(u8 action, const PointedThing& pointed)
2131 if(connectedAndInitialized() == false){
2132 infostream<<"Client::interact() "
2133 "cancelled (not connected)"
2138 std::ostringstream os(std::ios_base::binary);
2144 [5] u32 length of the next item
2145 [9] serialized PointedThing
2147 0: start digging (from undersurface) or use
2148 1: stop digging (all parameters ignored)
2149 2: digging completed
2150 3: place block or item (to abovesurface)
2153 writeU16(os, TOSERVER_INTERACT);
2154 writeU8(os, action);
2155 writeU16(os, getPlayerItem());
2156 std::ostringstream tmp_os(std::ios::binary);
2157 pointed.serialize(tmp_os);
2158 os<<serializeLongString(tmp_os.str());
2160 std::string s = os.str();
2161 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2164 Send(0, data, true);
2167 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2168 const std::map<std::string, std::string> &fields)
2170 std::ostringstream os(std::ios_base::binary);
2172 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2174 os<<serializeString(formname);
2175 writeU16(os, fields.size());
2176 for(std::map<std::string, std::string>::const_iterator
2177 i = fields.begin(); i != fields.end(); i++){
2178 const std::string &name = i->first;
2179 const std::string &value = i->second;
2180 os<<serializeString(name);
2181 os<<serializeLongString(value);
2185 std::string s = os.str();
2186 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2188 Send(0, data, true);
2191 void Client::sendInventoryFields(const std::string &formname,
2192 const std::map<std::string, std::string> &fields)
2194 std::ostringstream os(std::ios_base::binary);
2196 writeU16(os, TOSERVER_INVENTORY_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::sendInventoryAction(InventoryAction *a)
2216 std::ostringstream os(std::ios_base::binary);
2220 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2221 os.write((char*)buf, 2);
2226 std::string s = os.str();
2227 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2229 Send(0, data, true);
2232 void Client::sendChatMessage(const std::wstring &message)
2234 std::ostringstream os(std::ios_base::binary);
2238 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2239 os.write((char*)buf, 2);
2242 writeU16(buf, message.size());
2243 os.write((char*)buf, 2);
2246 for(u32 i=0; i<message.size(); i++)
2250 os.write((char*)buf, 2);
2254 std::string s = os.str();
2255 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2257 Send(0, data, true);
2260 void Client::sendChangePassword(const std::wstring oldpassword,
2261 const std::wstring newpassword)
2263 Player *player = m_env.getLocalPlayer();
2267 std::string playername = player->getName();
2268 std::string oldpwd = translatePassword(playername, oldpassword);
2269 std::string newpwd = translatePassword(playername, newpassword);
2271 std::ostringstream os(std::ios_base::binary);
2272 u8 buf[2+PASSWORD_SIZE*2];
2274 [0] u16 TOSERVER_PASSWORD
2275 [2] u8[28] old password
2276 [30] u8[28] new password
2279 writeU16(buf, TOSERVER_PASSWORD);
2280 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2282 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2283 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2285 buf[2+PASSWORD_SIZE-1] = 0;
2286 buf[30+PASSWORD_SIZE-1] = 0;
2287 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2290 std::string s = os.str();
2291 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2293 Send(0, data, true);
2297 void Client::sendDamage(u8 damage)
2299 DSTACK(__FUNCTION_NAME);
2300 std::ostringstream os(std::ios_base::binary);
2302 writeU16(os, TOSERVER_DAMAGE);
2303 writeU8(os, damage);
2306 std::string s = os.str();
2307 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2309 Send(0, data, true);
2312 void Client::sendRespawn()
2314 DSTACK(__FUNCTION_NAME);
2315 std::ostringstream os(std::ios_base::binary);
2317 writeU16(os, TOSERVER_RESPAWN);
2320 std::string s = os.str();
2321 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2323 Send(0, data, true);
2326 void Client::sendPlayerPos()
2328 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2330 LocalPlayer *myplayer = m_env.getLocalPlayer();
2331 if(myplayer == NULL)
2334 // Save bandwidth by only updating position when something changed
2335 if(myplayer->last_position == myplayer->getPosition() &&
2336 myplayer->last_speed == myplayer->getSpeed() &&
2337 myplayer->last_pitch == myplayer->getPitch() &&
2338 myplayer->last_yaw == myplayer->getYaw() &&
2339 myplayer->last_keyPressed == myplayer->keyPressed)
2342 myplayer->last_position = myplayer->getPosition();
2343 myplayer->last_speed = myplayer->getSpeed();
2344 myplayer->last_pitch = myplayer->getPitch();
2345 myplayer->last_yaw = myplayer->getYaw();
2346 myplayer->last_keyPressed = myplayer->keyPressed;
2350 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2351 our_peer_id = m_con.GetPeerID();
2354 // Set peer id if not set already
2355 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2356 myplayer->peer_id = our_peer_id;
2357 // Check that an existing peer_id is the same as the connection's
2358 assert(myplayer->peer_id == our_peer_id);
2360 v3f pf = myplayer->getPosition();
2361 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2362 v3f sf = myplayer->getSpeed();
2363 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2364 s32 pitch = myplayer->getPitch() * 100;
2365 s32 yaw = myplayer->getYaw() * 100;
2366 u32 keyPressed=myplayer->keyPressed;
2370 [2] v3s32 position*100
2371 [2+12] v3s32 speed*100
2372 [2+12+12] s32 pitch*100
2373 [2+12+12+4] s32 yaw*100
2374 [2+12+12+4+4] u32 keyPressed
2376 SharedBuffer<u8> data(2+12+12+4+4+4);
2377 writeU16(&data[0], TOSERVER_PLAYERPOS);
2378 writeV3S32(&data[2], position);
2379 writeV3S32(&data[2+12], speed);
2380 writeS32(&data[2+12+12], pitch);
2381 writeS32(&data[2+12+12+4], yaw);
2382 writeU32(&data[2+12+12+4+4], keyPressed);
2383 // Send as unreliable
2384 Send(0, data, false);
2387 void Client::sendPlayerItem(u16 item)
2389 Player *myplayer = m_env.getLocalPlayer();
2390 if(myplayer == NULL)
2393 u16 our_peer_id = m_con.GetPeerID();
2395 // Set peer id if not set already
2396 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2397 myplayer->peer_id = our_peer_id;
2398 // Check that an existing peer_id is the same as the connection's
2399 assert(myplayer->peer_id == our_peer_id);
2401 SharedBuffer<u8> data(2+2);
2402 writeU16(&data[0], TOSERVER_PLAYERITEM);
2403 writeU16(&data[2], item);
2406 Send(0, data, true);
2409 void Client::removeNode(v3s16 p)
2411 std::map<v3s16, MapBlock*> modified_blocks;
2415 //TimeTaker t("removeNodeAndUpdate", m_device);
2416 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2418 catch(InvalidPositionException &e)
2422 // add urgent task to update the modified node
2423 addUpdateMeshTaskForNode(p, false, true);
2425 for(std::map<v3s16, MapBlock * >::iterator
2426 i = modified_blocks.begin();
2427 i != modified_blocks.end(); ++i)
2429 addUpdateMeshTaskWithEdge(i->first);
2433 void Client::addNode(v3s16 p, MapNode n)
2435 TimeTaker timer1("Client::addNode()");
2437 std::map<v3s16, MapBlock*> modified_blocks;
2441 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2442 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2444 catch(InvalidPositionException &e)
2447 for(std::map<v3s16, MapBlock * >::iterator
2448 i = modified_blocks.begin();
2449 i != modified_blocks.end(); ++i)
2451 addUpdateMeshTaskWithEdge(i->first);
2455 void Client::setPlayerControl(PlayerControl &control)
2457 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2458 LocalPlayer *player = m_env.getLocalPlayer();
2459 assert(player != NULL);
2460 player->control = control;
2463 void Client::selectPlayerItem(u16 item)
2465 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2466 m_playeritem = item;
2467 m_inventory_updated = true;
2468 sendPlayerItem(item);
2471 // Returns true if the inventory of the local player has been
2472 // updated from the server. If it is true, it is set to false.
2473 bool Client::getLocalInventoryUpdated()
2475 // m_inventory_updated is behind envlock
2476 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2477 bool updated = m_inventory_updated;
2478 m_inventory_updated = false;
2482 // Copies the inventory of the local player to parameter
2483 void Client::getLocalInventory(Inventory &dst)
2485 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2486 Player *player = m_env.getLocalPlayer();
2487 assert(player != NULL);
2488 dst = player->inventory;
2491 Inventory* Client::getInventory(const InventoryLocation &loc)
2494 case InventoryLocation::UNDEFINED:
2497 case InventoryLocation::CURRENT_PLAYER:
2499 Player *player = m_env.getLocalPlayer();
2500 assert(player != NULL);
2501 return &player->inventory;
2504 case InventoryLocation::PLAYER:
2506 Player *player = m_env.getPlayer(loc.name.c_str());
2509 return &player->inventory;
2512 case InventoryLocation::NODEMETA:
2514 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2517 return meta->getInventory();
2520 case InventoryLocation::DETACHED:
2522 if(m_detached_inventories.count(loc.name) == 0)
2524 return m_detached_inventories[loc.name];
2532 void Client::inventoryAction(InventoryAction *a)
2535 Send it to the server
2537 sendInventoryAction(a);
2540 Predict some local inventory changes
2542 a->clientApply(this, this);
2545 ClientActiveObject * Client::getSelectedActiveObject(
2547 v3f from_pos_f_on_map,
2548 core::line3d<f32> shootline_on_map
2551 std::vector<DistanceSortedActiveObject> objects;
2553 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2555 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2558 // After this, the closest object is the first in the array.
2559 std::sort(objects.begin(), objects.end());
2561 for(u32 i=0; i<objects.size(); i++)
2563 ClientActiveObject *obj = objects[i].obj;
2565 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2566 if(selection_box == NULL)
2569 v3f pos = obj->getPosition();
2571 core::aabbox3d<f32> offsetted_box(
2572 selection_box->MinEdge + pos,
2573 selection_box->MaxEdge + pos
2576 if(offsetted_box.intersectsWithLine(shootline_on_map))
2578 //infostream<<"Returning selected object"<<std::endl;
2583 //infostream<<"No object selected; returning NULL."<<std::endl;
2587 void Client::printDebugInfo(std::ostream &os)
2589 //JMutexAutoLock lock1(m_fetchblock_mutex);
2590 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2592 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2593 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2594 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2598 std::list<std::string> Client::getConnectedPlayerNames()
2600 return m_env.getPlayerNames();
2603 float Client::getAnimationTime()
2605 return m_animation_time;
2608 int Client::getCrackLevel()
2610 return m_crack_level;
2613 void Client::setCrack(int level, v3s16 pos)
2615 int old_crack_level = m_crack_level;
2616 v3s16 old_crack_pos = m_crack_pos;
2618 m_crack_level = level;
2621 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2624 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2626 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2629 addUpdateMeshTaskForNode(pos, false, true);
2635 Player *player = m_env.getLocalPlayer();
2636 assert(player != NULL);
2640 bool Client::getChatMessage(std::wstring &message)
2642 if(m_chat_queue.size() == 0)
2644 message = m_chat_queue.pop_front();
2648 void Client::typeChatMessage(const std::wstring &message)
2650 // Discard empty line
2655 sendChatMessage(message);
2658 if (message[0] == L'/')
2660 m_chat_queue.push_back(
2661 (std::wstring)L"issued command: "+message);
2665 LocalPlayer *player = m_env.getLocalPlayer();
2666 assert(player != NULL);
2667 std::wstring name = narrow_to_wide(player->getName());
2668 m_chat_queue.push_back(
2669 (std::wstring)L"<"+name+L"> "+message);
2673 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2675 /*infostream<<"Client::addUpdateMeshTask(): "
2676 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2677 <<" ack_to_server="<<ack_to_server
2678 <<" urgent="<<urgent
2681 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2686 Create a task to update the mesh of the block
2689 MeshMakeData *data = new MeshMakeData(this);
2692 //TimeTaker timer("data fill");
2694 // Debug: 1-6ms, avg=2ms
2696 data->setCrack(m_crack_level, m_crack_pos);
2697 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2701 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2703 // Add task to queue
2704 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2706 /*infostream<<"Mesh update input queue size is "
2707 <<m_mesh_update_thread.m_queue_in.size()
2711 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2715 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2716 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2721 v3s16 p = blockpos + v3s16(0,0,0);
2722 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2723 addUpdateMeshTask(p, ack_to_server, urgent);
2725 catch(InvalidPositionException &e){}
2727 for (int i=0;i<6;i++)
2730 v3s16 p = blockpos + g_6dirs[i];
2731 addUpdateMeshTask(p, false, urgent);
2733 catch(InvalidPositionException &e){}
2737 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2741 infostream<<"Client::addUpdateMeshTaskForNode(): "
2742 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2746 v3s16 blockpos = getNodeBlockPos(nodepos);
2747 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2750 v3s16 p = blockpos + v3s16(0,0,0);
2751 addUpdateMeshTask(p, ack_to_server, urgent);
2753 catch(InvalidPositionException &e){}
2755 if(nodepos.X == blockpos_relative.X){
2757 v3s16 p = blockpos + v3s16(-1,0,0);
2758 addUpdateMeshTask(p, false, urgent);
2760 catch(InvalidPositionException &e){}
2762 if(nodepos.Y == blockpos_relative.Y){
2764 v3s16 p = blockpos + v3s16(0,-1,0);
2765 addUpdateMeshTask(p, false, urgent);
2767 catch(InvalidPositionException &e){}
2769 if(nodepos.Z == blockpos_relative.Z){
2771 v3s16 p = blockpos + v3s16(0,0,-1);
2772 addUpdateMeshTask(p, false, urgent);
2774 catch(InvalidPositionException &e){}
2778 ClientEvent Client::getClientEvent()
2780 if(m_client_event_queue.size() == 0)
2783 event.type = CE_NONE;
2786 return m_client_event_queue.pop_front();
2789 void Client::afterContentReceived()
2791 infostream<<"Client::afterContentReceived() started"<<std::endl;
2792 assert(m_itemdef_received);
2793 assert(m_nodedef_received);
2794 assert(texturesReceived());
2796 // remove the information about which checksum each texture
2798 m_media_name_sha1_map.clear();
2800 // Rebuild inherited images and recreate textures
2801 infostream<<"- Rebuilding images and textures"<<std::endl;
2802 m_tsrc->rebuildImagesAndTextures();
2804 // Update texture atlas
2805 infostream<<"- Updating texture atlas"<<std::endl;
2806 if(g_settings->getBool("enable_texture_atlas"))
2807 m_tsrc->buildMainAtlas(this);
2810 m_shsrc->rebuildShaders();
2812 // Update node aliases
2813 infostream<<"- Updating node aliases"<<std::endl;
2814 m_nodedef->updateAliases(m_itemdef);
2816 // Update node textures
2817 infostream<<"- Updating node textures"<<std::endl;
2818 m_nodedef->updateTextures(m_tsrc);
2820 // Preload item textures and meshes if configured to
2821 if(g_settings->getBool("preload_item_visuals"))
2823 verbosestream<<"Updating item textures and meshes"<<std::endl;
2824 std::set<std::string> names = m_itemdef->getAll();
2825 for(std::set<std::string>::const_iterator
2826 i = names.begin(); i != names.end(); ++i){
2827 // Asking for these caches the result
2828 m_itemdef->getInventoryTexture(*i, this);
2829 m_itemdef->getWieldMesh(*i, this);
2833 // Start mesh update thread after setting up content definitions
2834 infostream<<"- Starting mesh update thread"<<std::endl;
2835 m_mesh_update_thread.Start();
2837 infostream<<"Client::afterContentReceived() done"<<std::endl;
2840 float Client::getRTT(void)
2843 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2844 } catch(con::PeerNotFoundException &e){
2849 // IGameDef interface
2851 IItemDefManager* Client::getItemDefManager()
2855 INodeDefManager* Client::getNodeDefManager()
2859 ICraftDefManager* Client::getCraftDefManager()
2862 //return m_craftdef;
2864 ITextureSource* Client::getTextureSource()
2868 IShaderSource* Client::getShaderSource()
2872 u16 Client::allocateUnknownNodeId(const std::string &name)
2874 errorstream<<"Client::allocateUnknownNodeId(): "
2875 <<"Client cannot allocate node IDs"<<std::endl;
2877 return CONTENT_IGNORE;
2879 ISoundManager* Client::getSoundManager()
2883 MtEventManager* Client::getEventManager()