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 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2102 v2fdata = readV2F1000(is);
2103 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2104 sdata = deSerializeString(is);
2106 intdata = readU32(is);
2109 event.type = CE_HUDCHANGE;
2110 event.hudchange.id = id;
2111 event.hudchange.stat = (HudElementStat)stat;
2112 event.hudchange.v2fdata = new v2f(v2fdata);
2113 event.hudchange.sdata = new std::string(sdata);
2114 event.hudchange.data = intdata;
2115 m_client_event_queue.push_back(event);
2117 else if(command == TOCLIENT_HUD_SET_FLAGS)
2119 std::string datastring((char *)&data[2], datasize - 2);
2120 std::istringstream is(datastring, std::ios_base::binary);
2122 Player *player = m_env.getLocalPlayer();
2123 assert(player != NULL);
2125 u32 flags = readU32(is);
2126 u32 mask = readU32(is);
2128 player->hud_flags &= ~mask;
2129 player->hud_flags |= flags;
2133 infostream<<"Client: Ignoring unknown command "
2134 <<command<<std::endl;
2138 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2140 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2141 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2144 void Client::interact(u8 action, const PointedThing& pointed)
2146 if(connectedAndInitialized() == false){
2147 infostream<<"Client::interact() "
2148 "cancelled (not connected)"
2153 std::ostringstream os(std::ios_base::binary);
2159 [5] u32 length of the next item
2160 [9] serialized PointedThing
2162 0: start digging (from undersurface) or use
2163 1: stop digging (all parameters ignored)
2164 2: digging completed
2165 3: place block or item (to abovesurface)
2168 writeU16(os, TOSERVER_INTERACT);
2169 writeU8(os, action);
2170 writeU16(os, getPlayerItem());
2171 std::ostringstream tmp_os(std::ios::binary);
2172 pointed.serialize(tmp_os);
2173 os<<serializeLongString(tmp_os.str());
2175 std::string s = os.str();
2176 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2179 Send(0, data, true);
2182 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2183 const std::map<std::string, std::string> &fields)
2185 std::ostringstream os(std::ios_base::binary);
2187 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2189 os<<serializeString(formname);
2190 writeU16(os, fields.size());
2191 for(std::map<std::string, std::string>::const_iterator
2192 i = fields.begin(); i != fields.end(); i++){
2193 const std::string &name = i->first;
2194 const std::string &value = i->second;
2195 os<<serializeString(name);
2196 os<<serializeLongString(value);
2200 std::string s = os.str();
2201 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2203 Send(0, data, true);
2206 void Client::sendInventoryFields(const std::string &formname,
2207 const std::map<std::string, std::string> &fields)
2209 std::ostringstream os(std::ios_base::binary);
2211 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2212 os<<serializeString(formname);
2213 writeU16(os, fields.size());
2214 for(std::map<std::string, std::string>::const_iterator
2215 i = fields.begin(); i != fields.end(); i++){
2216 const std::string &name = i->first;
2217 const std::string &value = i->second;
2218 os<<serializeString(name);
2219 os<<serializeLongString(value);
2223 std::string s = os.str();
2224 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2226 Send(0, data, true);
2229 void Client::sendInventoryAction(InventoryAction *a)
2231 std::ostringstream os(std::ios_base::binary);
2235 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2236 os.write((char*)buf, 2);
2241 std::string s = os.str();
2242 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2244 Send(0, data, true);
2247 void Client::sendChatMessage(const std::wstring &message)
2249 std::ostringstream os(std::ios_base::binary);
2253 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2254 os.write((char*)buf, 2);
2257 writeU16(buf, message.size());
2258 os.write((char*)buf, 2);
2261 for(u32 i=0; i<message.size(); i++)
2265 os.write((char*)buf, 2);
2269 std::string s = os.str();
2270 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2272 Send(0, data, true);
2275 void Client::sendChangePassword(const std::wstring oldpassword,
2276 const std::wstring newpassword)
2278 Player *player = m_env.getLocalPlayer();
2282 std::string playername = player->getName();
2283 std::string oldpwd = translatePassword(playername, oldpassword);
2284 std::string newpwd = translatePassword(playername, newpassword);
2286 std::ostringstream os(std::ios_base::binary);
2287 u8 buf[2+PASSWORD_SIZE*2];
2289 [0] u16 TOSERVER_PASSWORD
2290 [2] u8[28] old password
2291 [30] u8[28] new password
2294 writeU16(buf, TOSERVER_PASSWORD);
2295 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2297 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2298 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2300 buf[2+PASSWORD_SIZE-1] = 0;
2301 buf[30+PASSWORD_SIZE-1] = 0;
2302 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2305 std::string s = os.str();
2306 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2308 Send(0, data, true);
2312 void Client::sendDamage(u8 damage)
2314 DSTACK(__FUNCTION_NAME);
2315 std::ostringstream os(std::ios_base::binary);
2317 writeU16(os, TOSERVER_DAMAGE);
2318 writeU8(os, damage);
2321 std::string s = os.str();
2322 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2324 Send(0, data, true);
2327 void Client::sendRespawn()
2329 DSTACK(__FUNCTION_NAME);
2330 std::ostringstream os(std::ios_base::binary);
2332 writeU16(os, TOSERVER_RESPAWN);
2335 std::string s = os.str();
2336 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2338 Send(0, data, true);
2341 void Client::sendPlayerPos()
2343 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2345 LocalPlayer *myplayer = m_env.getLocalPlayer();
2346 if(myplayer == NULL)
2349 // Save bandwidth by only updating position when something changed
2350 if(myplayer->last_position == myplayer->getPosition() &&
2351 myplayer->last_speed == myplayer->getSpeed() &&
2352 myplayer->last_pitch == myplayer->getPitch() &&
2353 myplayer->last_yaw == myplayer->getYaw() &&
2354 myplayer->last_keyPressed == myplayer->keyPressed)
2357 myplayer->last_position = myplayer->getPosition();
2358 myplayer->last_speed = myplayer->getSpeed();
2359 myplayer->last_pitch = myplayer->getPitch();
2360 myplayer->last_yaw = myplayer->getYaw();
2361 myplayer->last_keyPressed = myplayer->keyPressed;
2365 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2366 our_peer_id = m_con.GetPeerID();
2369 // Set peer id if not set already
2370 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2371 myplayer->peer_id = our_peer_id;
2372 // Check that an existing peer_id is the same as the connection's
2373 assert(myplayer->peer_id == our_peer_id);
2375 v3f pf = myplayer->getPosition();
2376 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2377 v3f sf = myplayer->getSpeed();
2378 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2379 s32 pitch = myplayer->getPitch() * 100;
2380 s32 yaw = myplayer->getYaw() * 100;
2381 u32 keyPressed=myplayer->keyPressed;
2385 [2] v3s32 position*100
2386 [2+12] v3s32 speed*100
2387 [2+12+12] s32 pitch*100
2388 [2+12+12+4] s32 yaw*100
2389 [2+12+12+4+4] u32 keyPressed
2391 SharedBuffer<u8> data(2+12+12+4+4+4);
2392 writeU16(&data[0], TOSERVER_PLAYERPOS);
2393 writeV3S32(&data[2], position);
2394 writeV3S32(&data[2+12], speed);
2395 writeS32(&data[2+12+12], pitch);
2396 writeS32(&data[2+12+12+4], yaw);
2397 writeU32(&data[2+12+12+4+4], keyPressed);
2398 // Send as unreliable
2399 Send(0, data, false);
2402 void Client::sendPlayerItem(u16 item)
2404 Player *myplayer = m_env.getLocalPlayer();
2405 if(myplayer == NULL)
2408 u16 our_peer_id = m_con.GetPeerID();
2410 // Set peer id if not set already
2411 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2412 myplayer->peer_id = our_peer_id;
2413 // Check that an existing peer_id is the same as the connection's
2414 assert(myplayer->peer_id == our_peer_id);
2416 SharedBuffer<u8> data(2+2);
2417 writeU16(&data[0], TOSERVER_PLAYERITEM);
2418 writeU16(&data[2], item);
2421 Send(0, data, true);
2424 void Client::removeNode(v3s16 p)
2426 std::map<v3s16, MapBlock*> modified_blocks;
2430 //TimeTaker t("removeNodeAndUpdate", m_device);
2431 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2433 catch(InvalidPositionException &e)
2437 // add urgent task to update the modified node
2438 addUpdateMeshTaskForNode(p, false, true);
2440 for(std::map<v3s16, MapBlock * >::iterator
2441 i = modified_blocks.begin();
2442 i != modified_blocks.end(); ++i)
2444 addUpdateMeshTaskWithEdge(i->first);
2448 void Client::addNode(v3s16 p, MapNode n)
2450 TimeTaker timer1("Client::addNode()");
2452 std::map<v3s16, MapBlock*> modified_blocks;
2456 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2457 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2459 catch(InvalidPositionException &e)
2462 for(std::map<v3s16, MapBlock * >::iterator
2463 i = modified_blocks.begin();
2464 i != modified_blocks.end(); ++i)
2466 addUpdateMeshTaskWithEdge(i->first);
2470 void Client::setPlayerControl(PlayerControl &control)
2472 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2473 LocalPlayer *player = m_env.getLocalPlayer();
2474 assert(player != NULL);
2475 player->control = control;
2478 void Client::selectPlayerItem(u16 item)
2480 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2481 m_playeritem = item;
2482 m_inventory_updated = true;
2483 sendPlayerItem(item);
2486 // Returns true if the inventory of the local player has been
2487 // updated from the server. If it is true, it is set to false.
2488 bool Client::getLocalInventoryUpdated()
2490 // m_inventory_updated is behind envlock
2491 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2492 bool updated = m_inventory_updated;
2493 m_inventory_updated = false;
2497 // Copies the inventory of the local player to parameter
2498 void Client::getLocalInventory(Inventory &dst)
2500 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2501 Player *player = m_env.getLocalPlayer();
2502 assert(player != NULL);
2503 dst = player->inventory;
2506 Inventory* Client::getInventory(const InventoryLocation &loc)
2509 case InventoryLocation::UNDEFINED:
2512 case InventoryLocation::CURRENT_PLAYER:
2514 Player *player = m_env.getLocalPlayer();
2515 assert(player != NULL);
2516 return &player->inventory;
2519 case InventoryLocation::PLAYER:
2521 Player *player = m_env.getPlayer(loc.name.c_str());
2524 return &player->inventory;
2527 case InventoryLocation::NODEMETA:
2529 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2532 return meta->getInventory();
2535 case InventoryLocation::DETACHED:
2537 if(m_detached_inventories.count(loc.name) == 0)
2539 return m_detached_inventories[loc.name];
2547 void Client::inventoryAction(InventoryAction *a)
2550 Send it to the server
2552 sendInventoryAction(a);
2555 Predict some local inventory changes
2557 a->clientApply(this, this);
2560 ClientActiveObject * Client::getSelectedActiveObject(
2562 v3f from_pos_f_on_map,
2563 core::line3d<f32> shootline_on_map
2566 std::vector<DistanceSortedActiveObject> objects;
2568 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2570 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2573 // After this, the closest object is the first in the array.
2574 std::sort(objects.begin(), objects.end());
2576 for(u32 i=0; i<objects.size(); i++)
2578 ClientActiveObject *obj = objects[i].obj;
2580 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2581 if(selection_box == NULL)
2584 v3f pos = obj->getPosition();
2586 core::aabbox3d<f32> offsetted_box(
2587 selection_box->MinEdge + pos,
2588 selection_box->MaxEdge + pos
2591 if(offsetted_box.intersectsWithLine(shootline_on_map))
2593 //infostream<<"Returning selected object"<<std::endl;
2598 //infostream<<"No object selected; returning NULL."<<std::endl;
2602 void Client::printDebugInfo(std::ostream &os)
2604 //JMutexAutoLock lock1(m_fetchblock_mutex);
2605 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2607 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2608 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2609 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2613 std::list<std::string> Client::getConnectedPlayerNames()
2615 return m_env.getPlayerNames();
2618 float Client::getAnimationTime()
2620 return m_animation_time;
2623 int Client::getCrackLevel()
2625 return m_crack_level;
2628 void Client::setCrack(int level, v3s16 pos)
2630 int old_crack_level = m_crack_level;
2631 v3s16 old_crack_pos = m_crack_pos;
2633 m_crack_level = level;
2636 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2639 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2641 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2644 addUpdateMeshTaskForNode(pos, false, true);
2650 Player *player = m_env.getLocalPlayer();
2651 assert(player != NULL);
2655 bool Client::getChatMessage(std::wstring &message)
2657 if(m_chat_queue.size() == 0)
2659 message = m_chat_queue.pop_front();
2663 void Client::typeChatMessage(const std::wstring &message)
2665 // Discard empty line
2670 sendChatMessage(message);
2673 if (message[0] == L'/')
2675 m_chat_queue.push_back(
2676 (std::wstring)L"issued command: "+message);
2680 LocalPlayer *player = m_env.getLocalPlayer();
2681 assert(player != NULL);
2682 std::wstring name = narrow_to_wide(player->getName());
2683 m_chat_queue.push_back(
2684 (std::wstring)L"<"+name+L"> "+message);
2688 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2690 /*infostream<<"Client::addUpdateMeshTask(): "
2691 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2692 <<" ack_to_server="<<ack_to_server
2693 <<" urgent="<<urgent
2696 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2701 Create a task to update the mesh of the block
2704 MeshMakeData *data = new MeshMakeData(this);
2707 //TimeTaker timer("data fill");
2709 // Debug: 1-6ms, avg=2ms
2711 data->setCrack(m_crack_level, m_crack_pos);
2712 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2716 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2718 // Add task to queue
2719 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2721 /*infostream<<"Mesh update input queue size is "
2722 <<m_mesh_update_thread.m_queue_in.size()
2726 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2730 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2731 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2736 v3s16 p = blockpos + v3s16(0,0,0);
2737 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2738 addUpdateMeshTask(p, ack_to_server, urgent);
2740 catch(InvalidPositionException &e){}
2742 for (int i=0;i<6;i++)
2745 v3s16 p = blockpos + g_6dirs[i];
2746 addUpdateMeshTask(p, false, urgent);
2748 catch(InvalidPositionException &e){}
2752 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2756 infostream<<"Client::addUpdateMeshTaskForNode(): "
2757 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2761 v3s16 blockpos = getNodeBlockPos(nodepos);
2762 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2765 v3s16 p = blockpos + v3s16(0,0,0);
2766 addUpdateMeshTask(p, ack_to_server, urgent);
2768 catch(InvalidPositionException &e){}
2770 if(nodepos.X == blockpos_relative.X){
2772 v3s16 p = blockpos + v3s16(-1,0,0);
2773 addUpdateMeshTask(p, false, urgent);
2775 catch(InvalidPositionException &e){}
2777 if(nodepos.Y == blockpos_relative.Y){
2779 v3s16 p = blockpos + v3s16(0,-1,0);
2780 addUpdateMeshTask(p, false, urgent);
2782 catch(InvalidPositionException &e){}
2784 if(nodepos.Z == blockpos_relative.Z){
2786 v3s16 p = blockpos + v3s16(0,0,-1);
2787 addUpdateMeshTask(p, false, urgent);
2789 catch(InvalidPositionException &e){}
2793 ClientEvent Client::getClientEvent()
2795 if(m_client_event_queue.size() == 0)
2798 event.type = CE_NONE;
2801 return m_client_event_queue.pop_front();
2804 void Client::afterContentReceived()
2806 infostream<<"Client::afterContentReceived() started"<<std::endl;
2807 assert(m_itemdef_received);
2808 assert(m_nodedef_received);
2809 assert(texturesReceived());
2811 // remove the information about which checksum each texture
2813 m_media_name_sha1_map.clear();
2815 // Rebuild inherited images and recreate textures
2816 infostream<<"- Rebuilding images and textures"<<std::endl;
2817 m_tsrc->rebuildImagesAndTextures();
2819 // Update texture atlas
2820 infostream<<"- Updating texture atlas"<<std::endl;
2821 if(g_settings->getBool("enable_texture_atlas"))
2822 m_tsrc->buildMainAtlas(this);
2825 m_shsrc->rebuildShaders();
2827 // Update node aliases
2828 infostream<<"- Updating node aliases"<<std::endl;
2829 m_nodedef->updateAliases(m_itemdef);
2831 // Update node textures
2832 infostream<<"- Updating node textures"<<std::endl;
2833 m_nodedef->updateTextures(m_tsrc);
2835 // Preload item textures and meshes if configured to
2836 if(g_settings->getBool("preload_item_visuals"))
2838 verbosestream<<"Updating item textures and meshes"<<std::endl;
2839 std::set<std::string> names = m_itemdef->getAll();
2840 for(std::set<std::string>::const_iterator
2841 i = names.begin(); i != names.end(); ++i){
2842 // Asking for these caches the result
2843 m_itemdef->getInventoryTexture(*i, this);
2844 m_itemdef->getWieldMesh(*i, this);
2848 // Start mesh update thread after setting up content definitions
2849 infostream<<"- Starting mesh update thread"<<std::endl;
2850 m_mesh_update_thread.Start();
2852 infostream<<"Client::afterContentReceived() done"<<std::endl;
2855 float Client::getRTT(void)
2858 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2859 } catch(con::PeerNotFoundException &e){
2864 // IGameDef interface
2866 IItemDefManager* Client::getItemDefManager()
2870 INodeDefManager* Client::getNodeDefManager()
2874 ICraftDefManager* Client::getCraftDefManager()
2877 //return m_craftdef;
2879 ITextureSource* Client::getTextureSource()
2883 IShaderSource* Client::getShaderSource()
2887 u16 Client::allocateUnknownNodeId(const std::string &name)
2889 errorstream<<"Client::allocateUnknownNodeId(): "
2890 <<"Client cannot allocate node IDs"<<std::endl;
2892 return CONTENT_IGNORE;
2894 ISoundManager* Client::getSoundManager()
2898 MtEventManager* Client::getEventManager()