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);
2119 infostream<<"Client: Ignoring unknown command "
2120 <<command<<std::endl;
2124 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2126 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2127 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2130 void Client::interact(u8 action, const PointedThing& pointed)
2132 if(connectedAndInitialized() == false){
2133 infostream<<"Client::interact() "
2134 "cancelled (not connected)"
2139 std::ostringstream os(std::ios_base::binary);
2145 [5] u32 length of the next item
2146 [9] serialized PointedThing
2148 0: start digging (from undersurface) or use
2149 1: stop digging (all parameters ignored)
2150 2: digging completed
2151 3: place block or item (to abovesurface)
2154 writeU16(os, TOSERVER_INTERACT);
2155 writeU8(os, action);
2156 writeU16(os, getPlayerItem());
2157 std::ostringstream tmp_os(std::ios::binary);
2158 pointed.serialize(tmp_os);
2159 os<<serializeLongString(tmp_os.str());
2161 std::string s = os.str();
2162 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2165 Send(0, data, true);
2168 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2169 const std::map<std::string, std::string> &fields)
2171 std::ostringstream os(std::ios_base::binary);
2173 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2175 os<<serializeString(formname);
2176 writeU16(os, fields.size());
2177 for(std::map<std::string, std::string>::const_iterator
2178 i = fields.begin(); i != fields.end(); i++){
2179 const std::string &name = i->first;
2180 const std::string &value = i->second;
2181 os<<serializeString(name);
2182 os<<serializeLongString(value);
2186 std::string s = os.str();
2187 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2189 Send(0, data, true);
2192 void Client::sendInventoryFields(const std::string &formname,
2193 const std::map<std::string, std::string> &fields)
2195 std::ostringstream os(std::ios_base::binary);
2197 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2198 os<<serializeString(formname);
2199 writeU16(os, fields.size());
2200 for(std::map<std::string, std::string>::const_iterator
2201 i = fields.begin(); i != fields.end(); i++){
2202 const std::string &name = i->first;
2203 const std::string &value = i->second;
2204 os<<serializeString(name);
2205 os<<serializeLongString(value);
2209 std::string s = os.str();
2210 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2212 Send(0, data, true);
2215 void Client::sendInventoryAction(InventoryAction *a)
2217 std::ostringstream os(std::ios_base::binary);
2221 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2222 os.write((char*)buf, 2);
2227 std::string s = os.str();
2228 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2230 Send(0, data, true);
2233 void Client::sendChatMessage(const std::wstring &message)
2235 std::ostringstream os(std::ios_base::binary);
2239 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2240 os.write((char*)buf, 2);
2243 writeU16(buf, message.size());
2244 os.write((char*)buf, 2);
2247 for(u32 i=0; i<message.size(); i++)
2251 os.write((char*)buf, 2);
2255 std::string s = os.str();
2256 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2258 Send(0, data, true);
2261 void Client::sendChangePassword(const std::wstring oldpassword,
2262 const std::wstring newpassword)
2264 Player *player = m_env.getLocalPlayer();
2268 std::string playername = player->getName();
2269 std::string oldpwd = translatePassword(playername, oldpassword);
2270 std::string newpwd = translatePassword(playername, newpassword);
2272 std::ostringstream os(std::ios_base::binary);
2273 u8 buf[2+PASSWORD_SIZE*2];
2275 [0] u16 TOSERVER_PASSWORD
2276 [2] u8[28] old password
2277 [30] u8[28] new password
2280 writeU16(buf, TOSERVER_PASSWORD);
2281 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2283 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2284 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2286 buf[2+PASSWORD_SIZE-1] = 0;
2287 buf[30+PASSWORD_SIZE-1] = 0;
2288 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2291 std::string s = os.str();
2292 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2294 Send(0, data, true);
2298 void Client::sendDamage(u8 damage)
2300 DSTACK(__FUNCTION_NAME);
2301 std::ostringstream os(std::ios_base::binary);
2303 writeU16(os, TOSERVER_DAMAGE);
2304 writeU8(os, damage);
2307 std::string s = os.str();
2308 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2310 Send(0, data, true);
2313 void Client::sendRespawn()
2315 DSTACK(__FUNCTION_NAME);
2316 std::ostringstream os(std::ios_base::binary);
2318 writeU16(os, TOSERVER_RESPAWN);
2321 std::string s = os.str();
2322 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2324 Send(0, data, true);
2327 void Client::sendPlayerPos()
2329 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2331 LocalPlayer *myplayer = m_env.getLocalPlayer();
2332 if(myplayer == NULL)
2335 // Save bandwidth by only updating position when something changed
2336 if(myplayer->last_position == myplayer->getPosition() &&
2337 myplayer->last_speed == myplayer->getSpeed() &&
2338 myplayer->last_pitch == myplayer->getPitch() &&
2339 myplayer->last_yaw == myplayer->getYaw() &&
2340 myplayer->last_keyPressed == myplayer->keyPressed)
2343 myplayer->last_position = myplayer->getPosition();
2344 myplayer->last_speed = myplayer->getSpeed();
2345 myplayer->last_pitch = myplayer->getPitch();
2346 myplayer->last_yaw = myplayer->getYaw();
2347 myplayer->last_keyPressed = myplayer->keyPressed;
2351 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2352 our_peer_id = m_con.GetPeerID();
2355 // Set peer id if not set already
2356 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2357 myplayer->peer_id = our_peer_id;
2358 // Check that an existing peer_id is the same as the connection's
2359 assert(myplayer->peer_id == our_peer_id);
2361 v3f pf = myplayer->getPosition();
2362 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2363 v3f sf = myplayer->getSpeed();
2364 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2365 s32 pitch = myplayer->getPitch() * 100;
2366 s32 yaw = myplayer->getYaw() * 100;
2367 u32 keyPressed=myplayer->keyPressed;
2371 [2] v3s32 position*100
2372 [2+12] v3s32 speed*100
2373 [2+12+12] s32 pitch*100
2374 [2+12+12+4] s32 yaw*100
2375 [2+12+12+4+4] u32 keyPressed
2377 SharedBuffer<u8> data(2+12+12+4+4+4);
2378 writeU16(&data[0], TOSERVER_PLAYERPOS);
2379 writeV3S32(&data[2], position);
2380 writeV3S32(&data[2+12], speed);
2381 writeS32(&data[2+12+12], pitch);
2382 writeS32(&data[2+12+12+4], yaw);
2383 writeU32(&data[2+12+12+4+4], keyPressed);
2384 // Send as unreliable
2385 Send(0, data, false);
2388 void Client::sendPlayerItem(u16 item)
2390 Player *myplayer = m_env.getLocalPlayer();
2391 if(myplayer == NULL)
2394 u16 our_peer_id = m_con.GetPeerID();
2396 // Set peer id if not set already
2397 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2398 myplayer->peer_id = our_peer_id;
2399 // Check that an existing peer_id is the same as the connection's
2400 assert(myplayer->peer_id == our_peer_id);
2402 SharedBuffer<u8> data(2+2);
2403 writeU16(&data[0], TOSERVER_PLAYERITEM);
2404 writeU16(&data[2], item);
2407 Send(0, data, true);
2410 void Client::removeNode(v3s16 p)
2412 std::map<v3s16, MapBlock*> modified_blocks;
2416 //TimeTaker t("removeNodeAndUpdate", m_device);
2417 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2419 catch(InvalidPositionException &e)
2423 // add urgent task to update the modified node
2424 addUpdateMeshTaskForNode(p, false, true);
2426 for(std::map<v3s16, MapBlock * >::iterator
2427 i = modified_blocks.begin();
2428 i != modified_blocks.end(); ++i)
2430 addUpdateMeshTaskWithEdge(i->first);
2434 void Client::addNode(v3s16 p, MapNode n)
2436 TimeTaker timer1("Client::addNode()");
2438 std::map<v3s16, MapBlock*> modified_blocks;
2442 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2443 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2445 catch(InvalidPositionException &e)
2448 for(std::map<v3s16, MapBlock * >::iterator
2449 i = modified_blocks.begin();
2450 i != modified_blocks.end(); ++i)
2452 addUpdateMeshTaskWithEdge(i->first);
2456 void Client::setPlayerControl(PlayerControl &control)
2458 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2459 LocalPlayer *player = m_env.getLocalPlayer();
2460 assert(player != NULL);
2461 player->control = control;
2464 void Client::selectPlayerItem(u16 item)
2466 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2467 m_playeritem = item;
2468 m_inventory_updated = true;
2469 sendPlayerItem(item);
2472 // Returns true if the inventory of the local player has been
2473 // updated from the server. If it is true, it is set to false.
2474 bool Client::getLocalInventoryUpdated()
2476 // m_inventory_updated is behind envlock
2477 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2478 bool updated = m_inventory_updated;
2479 m_inventory_updated = false;
2483 // Copies the inventory of the local player to parameter
2484 void Client::getLocalInventory(Inventory &dst)
2486 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2487 Player *player = m_env.getLocalPlayer();
2488 assert(player != NULL);
2489 dst = player->inventory;
2492 Inventory* Client::getInventory(const InventoryLocation &loc)
2495 case InventoryLocation::UNDEFINED:
2498 case InventoryLocation::CURRENT_PLAYER:
2500 Player *player = m_env.getLocalPlayer();
2501 assert(player != NULL);
2502 return &player->inventory;
2505 case InventoryLocation::PLAYER:
2507 Player *player = m_env.getPlayer(loc.name.c_str());
2510 return &player->inventory;
2513 case InventoryLocation::NODEMETA:
2515 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2518 return meta->getInventory();
2521 case InventoryLocation::DETACHED:
2523 if(m_detached_inventories.count(loc.name) == 0)
2525 return m_detached_inventories[loc.name];
2533 void Client::inventoryAction(InventoryAction *a)
2536 Send it to the server
2538 sendInventoryAction(a);
2541 Predict some local inventory changes
2543 a->clientApply(this, this);
2546 ClientActiveObject * Client::getSelectedActiveObject(
2548 v3f from_pos_f_on_map,
2549 core::line3d<f32> shootline_on_map
2552 std::vector<DistanceSortedActiveObject> objects;
2554 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2556 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2559 // After this, the closest object is the first in the array.
2560 std::sort(objects.begin(), objects.end());
2562 for(u32 i=0; i<objects.size(); i++)
2564 ClientActiveObject *obj = objects[i].obj;
2566 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2567 if(selection_box == NULL)
2570 v3f pos = obj->getPosition();
2572 core::aabbox3d<f32> offsetted_box(
2573 selection_box->MinEdge + pos,
2574 selection_box->MaxEdge + pos
2577 if(offsetted_box.intersectsWithLine(shootline_on_map))
2579 //infostream<<"Returning selected object"<<std::endl;
2584 //infostream<<"No object selected; returning NULL."<<std::endl;
2588 void Client::printDebugInfo(std::ostream &os)
2590 //JMutexAutoLock lock1(m_fetchblock_mutex);
2591 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2593 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2594 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2595 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2599 std::list<std::string> Client::getConnectedPlayerNames()
2601 return m_env.getPlayerNames();
2604 float Client::getAnimationTime()
2606 return m_animation_time;
2609 int Client::getCrackLevel()
2611 return m_crack_level;
2614 void Client::setCrack(int level, v3s16 pos)
2616 int old_crack_level = m_crack_level;
2617 v3s16 old_crack_pos = m_crack_pos;
2619 m_crack_level = level;
2622 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2625 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2627 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2630 addUpdateMeshTaskForNode(pos, false, true);
2636 Player *player = m_env.getLocalPlayer();
2637 assert(player != NULL);
2641 bool Client::getChatMessage(std::wstring &message)
2643 if(m_chat_queue.size() == 0)
2645 message = m_chat_queue.pop_front();
2649 void Client::typeChatMessage(const std::wstring &message)
2651 // Discard empty line
2656 sendChatMessage(message);
2659 if (message[0] == L'/')
2661 m_chat_queue.push_back(
2662 (std::wstring)L"issued command: "+message);
2666 LocalPlayer *player = m_env.getLocalPlayer();
2667 assert(player != NULL);
2668 std::wstring name = narrow_to_wide(player->getName());
2669 m_chat_queue.push_back(
2670 (std::wstring)L"<"+name+L"> "+message);
2674 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2676 /*infostream<<"Client::addUpdateMeshTask(): "
2677 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2678 <<" ack_to_server="<<ack_to_server
2679 <<" urgent="<<urgent
2682 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2687 Create a task to update the mesh of the block
2690 MeshMakeData *data = new MeshMakeData(this);
2693 //TimeTaker timer("data fill");
2695 // Debug: 1-6ms, avg=2ms
2697 data->setCrack(m_crack_level, m_crack_pos);
2698 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2702 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2704 // Add task to queue
2705 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2707 /*infostream<<"Mesh update input queue size is "
2708 <<m_mesh_update_thread.m_queue_in.size()
2712 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2716 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2717 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2722 v3s16 p = blockpos + v3s16(0,0,0);
2723 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2724 addUpdateMeshTask(p, ack_to_server, urgent);
2726 catch(InvalidPositionException &e){}
2728 for (int i=0;i<6;i++)
2731 v3s16 p = blockpos + g_6dirs[i];
2732 addUpdateMeshTask(p, false, urgent);
2734 catch(InvalidPositionException &e){}
2738 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2742 infostream<<"Client::addUpdateMeshTaskForNode(): "
2743 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2747 v3s16 blockpos = getNodeBlockPos(nodepos);
2748 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2751 v3s16 p = blockpos + v3s16(0,0,0);
2752 addUpdateMeshTask(p, ack_to_server, urgent);
2754 catch(InvalidPositionException &e){}
2756 if(nodepos.X == blockpos_relative.X){
2758 v3s16 p = blockpos + v3s16(-1,0,0);
2759 addUpdateMeshTask(p, false, urgent);
2761 catch(InvalidPositionException &e){}
2763 if(nodepos.Y == blockpos_relative.Y){
2765 v3s16 p = blockpos + v3s16(0,-1,0);
2766 addUpdateMeshTask(p, false, urgent);
2768 catch(InvalidPositionException &e){}
2770 if(nodepos.Z == blockpos_relative.Z){
2772 v3s16 p = blockpos + v3s16(0,0,-1);
2773 addUpdateMeshTask(p, false, urgent);
2775 catch(InvalidPositionException &e){}
2779 ClientEvent Client::getClientEvent()
2781 if(m_client_event_queue.size() == 0)
2784 event.type = CE_NONE;
2787 return m_client_event_queue.pop_front();
2790 void Client::afterContentReceived()
2792 infostream<<"Client::afterContentReceived() started"<<std::endl;
2793 assert(m_itemdef_received);
2794 assert(m_nodedef_received);
2795 assert(texturesReceived());
2797 // remove the information about which checksum each texture
2799 m_media_name_sha1_map.clear();
2801 // Rebuild inherited images and recreate textures
2802 infostream<<"- Rebuilding images and textures"<<std::endl;
2803 m_tsrc->rebuildImagesAndTextures();
2805 // Update texture atlas
2806 infostream<<"- Updating texture atlas"<<std::endl;
2807 if(g_settings->getBool("enable_texture_atlas"))
2808 m_tsrc->buildMainAtlas(this);
2811 m_shsrc->rebuildShaders();
2813 // Update node aliases
2814 infostream<<"- Updating node aliases"<<std::endl;
2815 m_nodedef->updateAliases(m_itemdef);
2817 // Update node textures
2818 infostream<<"- Updating node textures"<<std::endl;
2819 m_nodedef->updateTextures(m_tsrc);
2821 // Preload item textures and meshes if configured to
2822 if(g_settings->getBool("preload_item_visuals"))
2824 verbosestream<<"Updating item textures and meshes"<<std::endl;
2825 std::set<std::string> names = m_itemdef->getAll();
2826 for(std::set<std::string>::const_iterator
2827 i = names.begin(); i != names.end(); ++i){
2828 // Asking for these caches the result
2829 m_itemdef->getInventoryTexture(*i, this);
2830 m_itemdef->getWieldMesh(*i, this);
2834 // Start mesh update thread after setting up content definitions
2835 infostream<<"- Starting mesh update thread"<<std::endl;
2836 m_mesh_update_thread.Start();
2838 infostream<<"Client::afterContentReceived() done"<<std::endl;
2841 float Client::getRTT(void)
2844 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2845 } catch(con::PeerNotFoundException &e){
2850 // IGameDef interface
2852 IItemDefManager* Client::getItemDefManager()
2856 INodeDefManager* Client::getNodeDefManager()
2860 ICraftDefManager* Client::getCraftDefManager()
2863 //return m_craftdef;
2865 ITextureSource* Client::getTextureSource()
2869 IShaderSource* Client::getShaderSource()
2873 u16 Client::allocateUnknownNodeId(const std::string &name)
2875 errorstream<<"Client::allocateUnknownNodeId(): "
2876 <<"Client cannot allocate node IDs"<<std::endl;
2878 return CONTENT_IGNORE;
2880 ISoundManager* Client::getSoundManager()
2884 MtEventManager* Client::getEventManager()