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"
50 #include <curl/curl.h>
53 static std::string getMediaCacheDir()
55 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
62 QueuedMeshUpdate::QueuedMeshUpdate():
65 ack_block_to_server(false)
69 QueuedMeshUpdate::~QueuedMeshUpdate()
79 MeshUpdateQueue::MeshUpdateQueue()
84 MeshUpdateQueue::~MeshUpdateQueue()
86 JMutexAutoLock lock(m_mutex);
88 for(std::vector<QueuedMeshUpdate*>::iterator
90 i != m_queue.end(); i++)
92 QueuedMeshUpdate *q = *i;
98 peer_id=0 adds with nobody to send to
100 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
102 DSTACK(__FUNCTION_NAME);
106 JMutexAutoLock lock(m_mutex);
112 Find if block is already in queue.
113 If it is, update the data and quit.
115 for(std::vector<QueuedMeshUpdate*>::iterator
117 i != m_queue.end(); i++)
119 QueuedMeshUpdate *q = *i;
125 if(ack_block_to_server)
126 q->ack_block_to_server = true;
134 QueuedMeshUpdate *q = new QueuedMeshUpdate;
137 q->ack_block_to_server = ack_block_to_server;
138 m_queue.push_back(q);
141 // Returned pointer must be deleted
142 // Returns NULL if queue is empty
143 QueuedMeshUpdate * MeshUpdateQueue::pop()
145 JMutexAutoLock lock(m_mutex);
147 bool must_be_urgent = !m_urgents.empty();
148 for(std::vector<QueuedMeshUpdate*>::iterator
150 i != m_queue.end(); i++)
152 QueuedMeshUpdate *q = *i;
153 if(must_be_urgent && m_urgents.count(q->p) == 0)
156 m_urgents.erase(q->p);
166 void * MeshUpdateThread::Thread()
170 log_register_thread("MeshUpdateThread");
172 DSTACK(__FUNCTION_NAME);
174 BEGIN_DEBUG_EXCEPTION_HANDLER
178 /*// Wait for output queue to flush.
179 // Allow 2 in queue, this makes less frametime jitter.
180 // Umm actually, there is no much difference
181 if(m_queue_out.size() >= 2)
187 QueuedMeshUpdate *q = m_queue_in.pop();
194 ScopeProfiler sp(g_profiler, "Client: Mesh making");
196 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
197 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
206 r.ack_block_to_server = q->ack_block_to_server;
208 /*infostream<<"MeshUpdateThread: Processed "
209 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
212 m_queue_out.push_back(r);
217 END_DEBUG_EXCEPTION_HANDLER(errorstream)
222 void * MediaFetchThread::Thread()
226 log_register_thread("MediaFetchThread");
228 DSTACK(__FUNCTION_NAME);
230 BEGIN_DEBUG_EXCEPTION_HANDLER
235 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
236 i != m_file_requests.end(); ++i) {
237 curl = curl_easy_init();
239 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
240 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
241 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
242 std::ostringstream stream;
243 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
244 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
245 res = curl_easy_perform(curl);
246 if (res == CURLE_OK) {
247 std::string data = stream.str();
248 m_file_data.push_back(make_pair(i->name, data));
250 m_failed.push_back(*i);
251 infostream << "cURL request failed for " << i->name << std::endl;
253 curl_easy_cleanup(curl);
257 END_DEBUG_EXCEPTION_HANDLER(errorstream)
263 IrrlichtDevice *device,
264 const char *playername,
265 std::string password,
266 MapDrawControl &control,
267 IWritableTextureSource *tsrc,
268 IWritableShaderSource *shsrc,
269 IWritableItemDefManager *itemdef,
270 IWritableNodeDefManager *nodedef,
271 ISoundManager *sound,
272 MtEventManager *event
280 m_mesh_update_thread(this),
282 new ClientMap(this, this, control,
283 device->getSceneManager()->getRootSceneNode(),
284 device->getSceneManager(), 666),
285 device->getSceneManager(),
288 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
290 m_server_ser_ver(SER_FMT_VER_INVALID),
292 m_inventory_updated(false),
293 m_inventory_from_server(NULL),
294 m_inventory_from_server_age(0.0),
299 m_password(password),
300 m_access_denied(false),
301 m_media_cache(getMediaCacheDir()),
302 m_media_receive_started(false),
304 m_media_received_count(0),
305 m_itemdef_received(false),
306 m_nodedef_received(false),
307 m_time_of_day_set(false),
308 m_last_time_of_day_f(-1),
309 m_time_of_day_update_timer(0),
310 m_recommended_send_interval(0.1),
311 m_removed_sounds_check_timer(0)
313 m_packetcounter_timer = 0.0;
314 //m_delete_unused_sectors_timer = 0.0;
315 m_connection_reinit_timer = 0.0;
316 m_avg_rtt_timer = 0.0;
317 m_playerpos_send_timer = 0.0;
318 m_ignore_damage_timer = 0.0;
320 // Build main texture atlas, now that the GameDef exists (that is, us)
321 if(g_settings->getBool("enable_texture_atlas"))
322 m_tsrc->buildMainAtlas(this);
324 infostream<<"Not building texture atlas."<<std::endl;
330 Player *player = new LocalPlayer(this);
332 player->updateName(playername);
334 m_env.addPlayer(player);
337 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
338 m_media_fetch_threads.push_back(new MediaFetchThread(this));
344 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
348 m_mesh_update_thread.setRun(false);
349 while(m_mesh_update_thread.IsRunning())
352 delete m_inventory_from_server;
354 // Delete detached inventories
356 for(std::map<std::string, Inventory*>::iterator
357 i = m_detached_inventories.begin();
358 i != m_detached_inventories.end(); i++){
363 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
364 i != m_media_fetch_threads.end(); ++i)
367 // cleanup 3d model meshes on client shutdown
368 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
369 scene::IAnimatedMesh * mesh =
370 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
373 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
377 void Client::connect(Address address)
379 DSTACK(__FUNCTION_NAME);
380 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
381 m_con.SetTimeoutMs(0);
382 m_con.Connect(address);
385 bool Client::connectedAndInitialized()
387 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
389 if(m_con.Connected() == false)
392 if(m_server_ser_ver == SER_FMT_VER_INVALID)
398 void Client::step(float dtime)
400 DSTACK(__FUNCTION_NAME);
406 if(m_ignore_damage_timer > dtime)
407 m_ignore_damage_timer -= dtime;
409 m_ignore_damage_timer = 0.0;
411 m_animation_time += dtime;
412 if(m_animation_time > 60.0)
413 m_animation_time -= 60.0;
415 m_time_of_day_update_timer += dtime;
417 //infostream<<"Client steps "<<dtime<<std::endl;
420 //TimeTaker timer("ReceiveAll()", m_device);
426 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
428 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
429 m_con.RunTimeouts(dtime);
436 float &counter = m_packetcounter_timer;
442 infostream<<"Client packetcounter (20s):"<<std::endl;
443 m_packetcounter.print(infostream);
444 m_packetcounter.clear();
448 // Get connection status
449 bool connected = connectedAndInitialized();
454 Delete unused sectors
456 NOTE: This jams the game for a while because deleting sectors
460 float &counter = m_delete_unused_sectors_timer;
468 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
470 core::list<v3s16> deleted_blocks;
472 float delete_unused_sectors_timeout =
473 g_settings->getFloat("client_delete_unused_sectors_timeout");
475 // Delete sector blocks
476 /*u32 num = m_env.getMap().unloadUnusedData
477 (delete_unused_sectors_timeout,
478 true, &deleted_blocks);*/
480 // Delete whole sectors
481 m_env.getMap().unloadUnusedData
482 (delete_unused_sectors_timeout,
485 if(deleted_blocks.size() > 0)
487 /*infostream<<"Client: Deleted blocks of "<<num
488 <<" unused sectors"<<std::endl;*/
489 /*infostream<<"Client: Deleted "<<num
490 <<" unused sectors"<<std::endl;*/
496 // Env is locked so con can be locked.
497 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
499 core::list<v3s16>::Iterator i = deleted_blocks.begin();
500 core::list<v3s16> sendlist;
503 if(sendlist.size() == 255 || i == deleted_blocks.end())
505 if(sendlist.size() == 0)
514 u32 replysize = 2+1+6*sendlist.size();
515 SharedBuffer<u8> reply(replysize);
516 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
517 reply[2] = sendlist.size();
519 for(core::list<v3s16>::Iterator
520 j = sendlist.begin();
521 j != sendlist.end(); j++)
523 writeV3S16(&reply[2+1+6*k], *j);
526 m_con.Send(PEER_ID_SERVER, 1, reply, true);
528 if(i == deleted_blocks.end())
534 sendlist.push_back(*i);
542 if(connected == false)
544 float &counter = m_connection_reinit_timer;
550 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
552 Player *myplayer = m_env.getLocalPlayer();
553 assert(myplayer != NULL);
555 // Send TOSERVER_INIT
556 // [0] u16 TOSERVER_INIT
557 // [2] u8 SER_FMT_VER_HIGHEST
558 // [3] u8[20] player_name
559 // [23] u8[28] password (new in some version)
560 // [51] u16 minimum supported network protocol version (added sometime)
561 // [53] u16 maximum supported network protocol version (added later than the previous one)
562 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
563 writeU16(&data[0], TOSERVER_INIT);
564 writeU8(&data[2], SER_FMT_VER_HIGHEST);
566 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
567 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
569 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
572 memset((char*)&data[23], 0, PASSWORD_SIZE);
573 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
575 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
576 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
578 // Send as unreliable
579 Send(0, data, false);
582 // Not connected, return
587 Do stuff if connected
591 Run Map's timers and unload unused data
593 const float map_timer_and_unload_dtime = 5.25;
594 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
596 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
597 std::list<v3s16> deleted_blocks;
598 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
599 g_settings->getFloat("client_unload_unused_data_timeout"),
602 /*if(deleted_blocks.size() > 0)
603 infostream<<"Client: Unloaded "<<deleted_blocks.size()
604 <<" unused blocks"<<std::endl;*/
608 NOTE: This loop is intentionally iterated the way it is.
611 std::list<v3s16>::iterator i = deleted_blocks.begin();
612 std::list<v3s16> sendlist;
615 if(sendlist.size() == 255 || i == deleted_blocks.end())
617 if(sendlist.size() == 0)
626 u32 replysize = 2+1+6*sendlist.size();
627 SharedBuffer<u8> reply(replysize);
628 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
629 reply[2] = sendlist.size();
631 for(std::list<v3s16>::iterator
632 j = sendlist.begin();
633 j != sendlist.end(); ++j)
635 writeV3S16(&reply[2+1+6*k], *j);
638 m_con.Send(PEER_ID_SERVER, 1, reply, true);
640 if(i == deleted_blocks.end())
646 sendlist.push_back(*i);
656 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
658 // Control local player (0ms)
659 LocalPlayer *player = m_env.getLocalPlayer();
660 assert(player != NULL);
661 player->applyControl(dtime);
663 //TimeTaker envtimer("env step", m_device);
672 ClientEnvEvent event = m_env.getClientEvent();
673 if(event.type == CEE_NONE)
677 else if(event.type == CEE_PLAYER_DAMAGE)
679 if(m_ignore_damage_timer <= 0)
681 u8 damage = event.player_damage.amount;
683 if(event.player_damage.send_to_server)
686 // Add to ClientEvent queue
688 event.type = CE_PLAYER_DAMAGE;
689 event.player_damage.amount = damage;
690 m_client_event_queue.push_back(event);
700 float &counter = m_avg_rtt_timer;
705 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
706 // connectedAndInitialized() is true, peer exists.
707 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
708 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
713 Send player position to server
716 float &counter = m_playerpos_send_timer;
718 if(counter >= m_recommended_send_interval)
726 Replace updated meshes
729 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
731 //TimeTaker timer("** Processing mesh update result queue");
734 /*infostream<<"Mesh update result queue size is "
735 <<m_mesh_update_thread.m_queue_out.size()
738 int num_processed_meshes = 0;
739 while(!m_mesh_update_thread.m_queue_out.empty())
741 num_processed_meshes++;
742 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
743 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
746 //JMutexAutoLock lock(block->mesh_mutex);
748 // Delete the old mesh
749 if(block->mesh != NULL)
751 // TODO: Remove hardware buffers of meshbuffers of block->mesh
756 // Replace with the new mesh
757 block->mesh = r.mesh;
759 if(r.ack_block_to_server)
761 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
762 <<","<<r.p.Z<<")"<<std::endl;*/
773 u32 replysize = 2+1+6;
774 SharedBuffer<u8> reply(replysize);
775 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
777 writeV3S16(&reply[3], r.p);
779 m_con.Send(PEER_ID_SERVER, 1, reply, true);
782 if(num_processed_meshes > 0)
783 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
789 if (m_media_receive_started) {
790 bool all_stopped = true;
791 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
792 thread != m_media_fetch_threads.end(); ++thread) {
793 all_stopped &= !(*thread)->IsRunning();
794 while (!(*thread)->m_file_data.empty()) {
795 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
796 ++m_media_received_count;
798 bool success = loadMedia(out.second, out.first);
800 verbosestream<<"Client: Loaded received media: "
801 <<"\""<<out.first<<"\". Caching."<<std::endl;
803 infostream<<"Client: Failed to load received media: "
804 <<"\""<<out.first<<"\". Not caching."<<std::endl;
808 bool did = fs::CreateAllDirs(getMediaCacheDir());
810 errorstream<<"Could not create media cache directory"
815 std::map<std::string, std::string>::iterator n;
816 n = m_media_name_sha1_map.find(out.first);
817 if(n == m_media_name_sha1_map.end())
818 errorstream<<"The server sent a file that has not "
819 <<"been announced."<<std::endl;
821 m_media_cache.update_sha1(out.second);
826 std::list<MediaRequest> fetch_failed;
827 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
828 thread != m_media_fetch_threads.end(); ++thread) {
829 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
830 request != (*thread)->m_failed.end(); ++request)
831 fetch_failed.push_back(*request);
832 (*thread)->m_failed.clear();
834 if (fetch_failed.size() > 0) {
835 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
836 << "Requesting them the usual way." << std::endl;
837 request_media(fetch_failed);
843 If the server didn't update the inventory in a while, revert
844 the local inventory (so the player notices the lag problem
845 and knows something is wrong).
847 if(m_inventory_from_server)
849 float interval = 10.0;
850 float count_before = floor(m_inventory_from_server_age / interval);
852 m_inventory_from_server_age += dtime;
854 float count_after = floor(m_inventory_from_server_age / interval);
856 if(count_after != count_before)
858 // Do this every <interval> seconds after TOCLIENT_INVENTORY
859 // Reset the locally changed inventory to the authoritative inventory
860 Player *player = m_env.getLocalPlayer();
861 player->inventory = *m_inventory_from_server;
862 m_inventory_updated = true;
867 Update positions of sounds attached to objects
870 for(std::map<int, u16>::iterator
871 i = m_sounds_to_objects.begin();
872 i != m_sounds_to_objects.end(); i++)
874 int client_id = i->first;
875 u16 object_id = i->second;
876 ClientActiveObject *cao = m_env.getActiveObject(object_id);
879 v3f pos = cao->getPosition();
880 m_sound->updateSoundPosition(client_id, pos);
885 Handle removed remotely initiated sounds
887 m_removed_sounds_check_timer += dtime;
888 if(m_removed_sounds_check_timer >= 2.32)
890 m_removed_sounds_check_timer = 0;
891 // Find removed sounds and clear references to them
892 std::set<s32> removed_server_ids;
893 for(std::map<s32, int>::iterator
894 i = m_sounds_server_to_client.begin();
895 i != m_sounds_server_to_client.end();)
897 s32 server_id = i->first;
898 int client_id = i->second;
900 if(!m_sound->soundExists(client_id)){
901 m_sounds_server_to_client.erase(server_id);
902 m_sounds_client_to_server.erase(client_id);
903 m_sounds_to_objects.erase(client_id);
904 removed_server_ids.insert(server_id);
908 if(removed_server_ids.size() != 0)
910 std::ostringstream os(std::ios_base::binary);
911 writeU16(os, TOSERVER_REMOVED_SOUNDS);
912 writeU16(os, removed_server_ids.size());
913 for(std::set<s32>::iterator i = removed_server_ids.begin();
914 i != removed_server_ids.end(); i++)
916 std::string s = os.str();
917 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
924 bool Client::loadMedia(const std::string &data, const std::string &filename)
926 // Silly irrlicht's const-incorrectness
927 Buffer<char> data_rw(data.c_str(), data.size());
931 const char *image_ext[] = {
932 ".png", ".jpg", ".bmp", ".tga",
933 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
936 name = removeStringEnd(filename, image_ext);
939 verbosestream<<"Client: Attempting to load image "
940 <<"file \""<<filename<<"\""<<std::endl;
942 io::IFileSystem *irrfs = m_device->getFileSystem();
943 video::IVideoDriver *vdrv = m_device->getVideoDriver();
945 // Create an irrlicht memory file
946 io::IReadFile *rfile = irrfs->createMemoryReadFile(
947 *data_rw, data_rw.getSize(), "_tempreadfile");
950 video::IImage *img = vdrv->createImageFromFile(rfile);
952 errorstream<<"Client: Cannot create image from data of "
953 <<"file \""<<filename<<"\""<<std::endl;
958 m_tsrc->insertSourceImage(filename, img);
965 const char *sound_ext[] = {
966 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
967 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
970 name = removeStringEnd(filename, sound_ext);
973 verbosestream<<"Client: Attempting to load sound "
974 <<"file \""<<filename<<"\""<<std::endl;
975 m_sound->loadSoundData(name, data);
979 const char *model_ext[] = {
980 ".x", ".b3d", ".md2", ".obj",
983 name = removeStringEnd(filename, model_ext);
986 verbosestream<<"Client: Storing model into Irrlicht: "
987 <<"\""<<filename<<"\""<<std::endl;
988 scene::ISceneManager *smgr = m_device->getSceneManager();
990 //check if mesh was already cached
991 scene::IAnimatedMesh *mesh =
992 smgr->getMeshCache()->getMeshByName(filename.c_str());
995 errorstream << "Multiple models with name: " << filename.c_str() <<
996 " found replacing previous model!" << std::endl;
998 smgr->getMeshCache()->removeMesh(mesh);
1002 io::IFileSystem *irrfs = m_device->getFileSystem();
1003 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1004 *data_rw, data_rw.getSize(), filename.c_str());
1007 mesh = smgr->getMesh(rfile);
1008 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1013 errorstream<<"Client: Don't know how to load file \""
1014 <<filename<<"\""<<std::endl;
1018 // Virtual methods from con::PeerHandler
1019 void Client::peerAdded(con::Peer *peer)
1021 infostream<<"Client::peerAdded(): peer->id="
1022 <<peer->id<<std::endl;
1024 void Client::deletingPeer(con::Peer *peer, bool timeout)
1026 infostream<<"Client::deletingPeer(): "
1027 "Server Peer is getting deleted "
1028 <<"(timeout="<<timeout<<")"<<std::endl;
1033 u16 number of files requested
1039 void Client::request_media(const std::list<MediaRequest> &file_requests)
1041 std::ostringstream os(std::ios_base::binary);
1042 writeU16(os, TOSERVER_REQUEST_MEDIA);
1043 writeU16(os, file_requests.size());
1045 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1046 i != file_requests.end(); ++i) {
1047 os<<serializeString(i->name);
1051 std::string s = os.str();
1052 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1054 Send(0, data, true);
1055 infostream<<"Client: Sending media request list to server ("
1056 <<file_requests.size()<<" files)"<<std::endl;
1059 void Client::ReceiveAll()
1061 DSTACK(__FUNCTION_NAME);
1062 u32 start_ms = porting::getTimeMs();
1065 // Limit time even if there would be huge amounts of data to
1067 if(porting::getTimeMs() > start_ms + 100)
1072 g_profiler->graphAdd("client_received_packets", 1);
1074 catch(con::NoIncomingDataException &e)
1078 catch(con::InvalidIncomingDataException &e)
1080 infostream<<"Client::ReceiveAll(): "
1081 "InvalidIncomingDataException: what()="
1082 <<e.what()<<std::endl;
1087 void Client::Receive()
1089 DSTACK(__FUNCTION_NAME);
1090 SharedBuffer<u8> data;
1094 //TimeTaker t1("con mutex and receive", m_device);
1095 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1096 datasize = m_con.Receive(sender_peer_id, data);
1098 //TimeTaker t1("ProcessData", m_device);
1099 ProcessData(*data, datasize, sender_peer_id);
1103 sender_peer_id given to this shall be quaranteed to be a valid peer
1105 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1107 DSTACK(__FUNCTION_NAME);
1109 // Ignore packets that don't even fit a command
1112 m_packetcounter.add(60000);
1116 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1118 //infostream<<"Client: received command="<<command<<std::endl;
1119 m_packetcounter.add((u16)command);
1122 If this check is removed, be sure to change the queue
1123 system to know the ids
1125 if(sender_peer_id != PEER_ID_SERVER)
1127 infostream<<"Client::ProcessData(): Discarding data not "
1128 "coming from server: peer_id="<<sender_peer_id
1133 u8 ser_version = m_server_ser_ver;
1135 //infostream<<"Client received command="<<(int)command<<std::endl;
1137 if(command == TOCLIENT_INIT)
1142 u8 deployed = data[2];
1144 infostream<<"Client: TOCLIENT_INIT received with "
1145 "deployed="<<((int)deployed&0xff)<<std::endl;
1147 if(deployed < SER_FMT_VER_LOWEST
1148 || deployed > SER_FMT_VER_HIGHEST)
1150 infostream<<"Client: TOCLIENT_INIT: Server sent "
1151 <<"unsupported ser_fmt_ver"<<std::endl;
1155 m_server_ser_ver = deployed;
1157 // Get player position
1158 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1159 if(datasize >= 2+1+6)
1160 playerpos_s16 = readV3S16(&data[2+1]);
1161 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1164 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1166 // Set player position
1167 Player *player = m_env.getLocalPlayer();
1168 assert(player != NULL);
1169 player->setPosition(playerpos_f);
1172 if(datasize >= 2+1+6+8)
1175 m_map_seed = readU64(&data[2+1+6]);
1176 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1179 if(datasize >= 2+1+6+8+4)
1182 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1183 infostream<<"Client: received recommended send interval "
1184 <<m_recommended_send_interval<<std::endl;
1189 SharedBuffer<u8> reply(replysize);
1190 writeU16(&reply[0], TOSERVER_INIT2);
1192 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1197 if(command == TOCLIENT_ACCESS_DENIED)
1199 // The server didn't like our password. Note, this needs
1200 // to be processed even if the serialisation format has
1201 // not been agreed yet, the same as TOCLIENT_INIT.
1202 m_access_denied = true;
1203 m_access_denied_reason = L"Unknown";
1206 std::string datastring((char*)&data[2], datasize-2);
1207 std::istringstream is(datastring, std::ios_base::binary);
1208 m_access_denied_reason = deSerializeWideString(is);
1213 if(ser_version == SER_FMT_VER_INVALID)
1215 infostream<<"Client: Server serialization"
1216 " format invalid or not initialized."
1217 " Skipping incoming command="<<command<<std::endl;
1221 // Just here to avoid putting the two if's together when
1222 // making some copypasta
1225 if(command == TOCLIENT_REMOVENODE)
1230 p.X = readS16(&data[2]);
1231 p.Y = readS16(&data[4]);
1232 p.Z = readS16(&data[6]);
1234 //TimeTaker t1("TOCLIENT_REMOVENODE");
1238 else if(command == TOCLIENT_ADDNODE)
1240 if(datasize < 8 + MapNode::serializedLength(ser_version))
1244 p.X = readS16(&data[2]);
1245 p.Y = readS16(&data[4]);
1246 p.Z = readS16(&data[6]);
1248 //TimeTaker t1("TOCLIENT_ADDNODE");
1251 n.deSerialize(&data[8], ser_version);
1255 else if(command == TOCLIENT_BLOCKDATA)
1257 // Ignore too small packet
1262 p.X = readS16(&data[2]);
1263 p.Y = readS16(&data[4]);
1264 p.Z = readS16(&data[6]);
1266 /*infostream<<"Client: Thread: BLOCKDATA for ("
1267 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1268 /*infostream<<"Client: Thread: BLOCKDATA for ("
1269 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1271 std::string datastring((char*)&data[8], datasize-8);
1272 std::istringstream istr(datastring, std::ios_base::binary);
1277 v2s16 p2d(p.X, p.Z);
1278 sector = m_env.getMap().emergeSector(p2d);
1280 assert(sector->getPos() == p2d);
1282 //TimeTaker timer("MapBlock deSerialize");
1285 block = sector->getBlockNoCreateNoEx(p.Y);
1289 Update an existing block
1291 //infostream<<"Updating"<<std::endl;
1292 block->deSerialize(istr, ser_version, false);
1299 //infostream<<"Creating new"<<std::endl;
1300 block = new MapBlock(&m_env.getMap(), p, this);
1301 block->deSerialize(istr, ser_version, false);
1302 sector->insertBlock(block);
1316 u32 replysize = 2+1+6;
1317 SharedBuffer<u8> reply(replysize);
1318 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1320 writeV3S16(&reply[3], p);
1322 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1326 Add it to mesh update queue and set it to be acknowledged after update.
1328 //infostream<<"Adding mesh update task for received block"<<std::endl;
1329 addUpdateMeshTaskWithEdge(p, true);
1331 else if(command == TOCLIENT_INVENTORY)
1336 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1339 //TimeTaker t2("mutex locking", m_device);
1340 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1343 //TimeTaker t3("istringstream init", m_device);
1344 std::string datastring((char*)&data[2], datasize-2);
1345 std::istringstream is(datastring, std::ios_base::binary);
1348 //m_env.printPlayers(infostream);
1350 //TimeTaker t4("player get", m_device);
1351 Player *player = m_env.getLocalPlayer();
1352 assert(player != NULL);
1355 //TimeTaker t1("inventory.deSerialize()", m_device);
1356 player->inventory.deSerialize(is);
1359 m_inventory_updated = true;
1361 delete m_inventory_from_server;
1362 m_inventory_from_server = new Inventory(player->inventory);
1363 m_inventory_from_server_age = 0.0;
1365 //infostream<<"Client got player inventory:"<<std::endl;
1366 //player->inventory.print(infostream);
1369 else if(command == TOCLIENT_TIME_OF_DAY)
1374 u16 time_of_day = readU16(&data[2]);
1375 time_of_day = time_of_day % 24000;
1376 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1377 float time_speed = 0;
1378 if(datasize >= 2 + 2 + 4){
1379 time_speed = readF1000(&data[4]);
1381 // Old message; try to approximate speed of time by ourselves
1382 float time_of_day_f = (float)time_of_day / 24000.0;
1383 float tod_diff_f = 0;
1384 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1385 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1387 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1388 m_last_time_of_day_f = time_of_day_f;
1389 float time_diff = m_time_of_day_update_timer;
1390 m_time_of_day_update_timer = 0;
1391 if(m_time_of_day_set){
1392 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1393 infostream<<"Client: Measured time_of_day speed (old format): "
1394 <<time_speed<<" tod_diff_f="<<tod_diff_f
1395 <<" time_diff="<<time_diff<<std::endl;
1399 // Update environment
1400 m_env.setTimeOfDay(time_of_day);
1401 m_env.setTimeOfDaySpeed(time_speed);
1402 m_time_of_day_set = true;
1404 u32 dr = m_env.getDayNightRatio();
1405 verbosestream<<"Client: time_of_day="<<time_of_day
1406 <<" time_speed="<<time_speed
1407 <<" dr="<<dr<<std::endl;
1409 else if(command == TOCLIENT_CHAT_MESSAGE)
1417 std::string datastring((char*)&data[2], datasize-2);
1418 std::istringstream is(datastring, std::ios_base::binary);
1421 is.read((char*)buf, 2);
1422 u16 len = readU16(buf);
1424 std::wstring message;
1425 for(u16 i=0; i<len; i++)
1427 is.read((char*)buf, 2);
1428 message += (wchar_t)readU16(buf);
1431 /*infostream<<"Client received chat message: "
1432 <<wide_to_narrow(message)<<std::endl;*/
1434 m_chat_queue.push_back(message);
1436 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1438 //if(g_settings->getBool("enable_experimental"))
1442 u16 count of removed objects
1443 for all removed objects {
1446 u16 count of added objects
1447 for all added objects {
1450 u32 initialization data length
1451 string initialization data
1456 // Get all data except the command number
1457 std::string datastring((char*)&data[2], datasize-2);
1458 // Throw them in an istringstream
1459 std::istringstream is(datastring, std::ios_base::binary);
1463 // Read removed objects
1465 u16 removed_count = readU16((u8*)buf);
1466 for(u16 i=0; i<removed_count; i++)
1469 u16 id = readU16((u8*)buf);
1472 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1473 m_env.removeActiveObject(id);
1477 // Read added objects
1479 u16 added_count = readU16((u8*)buf);
1480 for(u16 i=0; i<added_count; i++)
1483 u16 id = readU16((u8*)buf);
1485 u8 type = readU8((u8*)buf);
1486 std::string data = deSerializeLongString(is);
1489 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1490 m_env.addActiveObject(id, type, data);
1495 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1497 //if(g_settings->getBool("enable_experimental"))
1509 // Get all data except the command number
1510 std::string datastring((char*)&data[2], datasize-2);
1511 // Throw them in an istringstream
1512 std::istringstream is(datastring, std::ios_base::binary);
1514 while(is.eof() == false)
1518 u16 id = readU16((u8*)buf);
1522 u16 message_size = readU16((u8*)buf);
1523 std::string message;
1524 message.reserve(message_size);
1525 for(u16 i=0; i<message_size; i++)
1528 message.append(buf, 1);
1530 // Pass on to the environment
1532 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1533 m_env.processActiveObjectMessage(id, message);
1538 else if(command == TOCLIENT_MOVEMENT)
1540 std::string datastring((char*)&data[2], datasize-2);
1541 std::istringstream is(datastring, std::ios_base::binary);
1542 Player *player = m_env.getLocalPlayer();
1543 assert(player != NULL);
1545 player->movement_acceleration_default = readF1000(is) * BS;
1546 player->movement_acceleration_air = readF1000(is) * BS;
1547 player->movement_acceleration_fast = readF1000(is) * BS;
1548 player->movement_speed_walk = readF1000(is) * BS;
1549 player->movement_speed_crouch = readF1000(is) * BS;
1550 player->movement_speed_fast = readF1000(is) * BS;
1551 player->movement_speed_climb = readF1000(is) * BS;
1552 player->movement_speed_jump = readF1000(is) * BS;
1553 player->movement_liquid_fluidity = readF1000(is) * BS;
1554 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1555 player->movement_liquid_sink = readF1000(is) * BS;
1556 player->movement_gravity = readF1000(is) * BS;
1558 else if(command == TOCLIENT_HP)
1560 std::string datastring((char*)&data[2], datasize-2);
1561 std::istringstream is(datastring, std::ios_base::binary);
1562 Player *player = m_env.getLocalPlayer();
1563 assert(player != NULL);
1564 u8 oldhp = player->hp;
1570 // Add to ClientEvent queue
1572 event.type = CE_PLAYER_DAMAGE;
1573 event.player_damage.amount = oldhp - hp;
1574 m_client_event_queue.push_back(event);
1577 else if(command == TOCLIENT_MOVE_PLAYER)
1579 std::string datastring((char*)&data[2], datasize-2);
1580 std::istringstream is(datastring, std::ios_base::binary);
1581 Player *player = m_env.getLocalPlayer();
1582 assert(player != NULL);
1583 v3f pos = readV3F1000(is);
1584 f32 pitch = readF1000(is);
1585 f32 yaw = readF1000(is);
1586 player->setPosition(pos);
1587 /*player->setPitch(pitch);
1588 player->setYaw(yaw);*/
1590 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1591 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1597 Add to ClientEvent queue.
1598 This has to be sent to the main program because otherwise
1599 it would just force the pitch and yaw values to whatever
1600 the camera points to.
1603 event.type = CE_PLAYER_FORCE_MOVE;
1604 event.player_force_move.pitch = pitch;
1605 event.player_force_move.yaw = yaw;
1606 m_client_event_queue.push_back(event);
1608 // Ignore damage for a few seconds, so that the player doesn't
1609 // get damage from falling on ground
1610 m_ignore_damage_timer = 3.0;
1612 else if(command == TOCLIENT_PLAYERITEM)
1614 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1616 else if(command == TOCLIENT_DEATHSCREEN)
1618 std::string datastring((char*)&data[2], datasize-2);
1619 std::istringstream is(datastring, std::ios_base::binary);
1621 bool set_camera_point_target = readU8(is);
1622 v3f camera_point_target = readV3F1000(is);
1625 event.type = CE_DEATHSCREEN;
1626 event.deathscreen.set_camera_point_target = set_camera_point_target;
1627 event.deathscreen.camera_point_target_x = camera_point_target.X;
1628 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1629 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1630 m_client_event_queue.push_back(event);
1632 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1634 std::string datastring((char*)&data[2], datasize-2);
1635 std::istringstream is(datastring, std::ios_base::binary);
1637 // Mesh update thread must be stopped while
1638 // updating content definitions
1639 assert(!m_mesh_update_thread.IsRunning());
1641 int num_files = readU16(is);
1643 infostream<<"Client: Received media announcement: packet size: "
1644 <<datasize<<std::endl;
1646 std::list<MediaRequest> file_requests;
1648 for(int i=0; i<num_files; i++)
1650 //read file from cache
1651 std::string name = deSerializeString(is);
1652 std::string sha1_base64 = deSerializeString(is);
1654 // if name contains illegal characters, ignore the file
1655 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1656 errorstream<<"Client: ignoring illegal file name "
1657 <<"sent by server: \""<<name<<"\""<<std::endl;
1661 std::string sha1_raw = base64_decode(sha1_base64);
1662 std::string sha1_hex = hex_encode(sha1_raw);
1663 std::ostringstream tmp_os(std::ios_base::binary);
1664 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1665 m_media_name_sha1_map[name] = sha1_raw;
1667 // If found in cache, try to load it from there
1670 bool success = loadMedia(tmp_os.str(), name);
1672 verbosestream<<"Client: Loaded cached media: "
1673 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1676 infostream<<"Client: Failed to load cached media: "
1677 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1680 // Didn't load from cache; queue it to be requested
1681 verbosestream<<"Client: Adding file to request list: \""
1682 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1683 file_requests.push_back(MediaRequest(name));
1686 std::string remote_media = "";
1688 remote_media = deSerializeString(is);
1690 catch(SerializationError) {
1691 // not supported by server or turned off
1694 m_media_count = file_requests.size();
1695 m_media_receive_started = true;
1697 if (remote_media == "" || !USE_CURL) {
1698 request_media(file_requests);
1701 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1702 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1703 i != file_requests.end(); ++i) {
1704 (*cur)->m_file_requests.push_back(*i);
1706 if (cur == m_media_fetch_threads.end())
1707 cur = m_media_fetch_threads.begin();
1709 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1710 i != m_media_fetch_threads.end(); ++i) {
1711 (*i)->m_remote_url = remote_media;
1716 // notify server we received everything
1717 std::ostringstream os(std::ios_base::binary);
1718 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1719 std::string s = os.str();
1720 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1722 Send(0, data, true);
1725 event.type = CE_TEXTURES_UPDATED;
1726 m_client_event_queue.push_back(event);
1728 else if(command == TOCLIENT_MEDIA)
1730 if (m_media_count == 0)
1732 std::string datastring((char*)&data[2], datasize-2);
1733 std::istringstream is(datastring, std::ios_base::binary);
1735 // Mesh update thread must be stopped while
1736 // updating content definitions
1737 assert(!m_mesh_update_thread.IsRunning());
1741 u16 total number of file bunches
1742 u16 index of this bunch
1743 u32 number of files in this bunch
1751 int num_bunches = readU16(is);
1752 int bunch_i = readU16(is);
1753 int num_files = readU32(is);
1754 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1755 <<num_bunches<<" files="<<num_files
1756 <<" size="<<datasize<<std::endl;
1757 for(int i=0; i<num_files; i++){
1758 m_media_received_count++;
1759 std::string name = deSerializeString(is);
1760 std::string data = deSerializeLongString(is);
1762 // if name contains illegal characters, ignore the file
1763 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1764 errorstream<<"Client: ignoring illegal file name "
1765 <<"sent by server: \""<<name<<"\""<<std::endl;
1769 bool success = loadMedia(data, name);
1771 verbosestream<<"Client: Loaded received media: "
1772 <<"\""<<name<<"\". Caching."<<std::endl;
1774 infostream<<"Client: Failed to load received media: "
1775 <<"\""<<name<<"\". Not caching."<<std::endl;
1779 bool did = fs::CreateAllDirs(getMediaCacheDir());
1781 errorstream<<"Could not create media cache directory"
1786 std::map<std::string, std::string>::iterator n;
1787 n = m_media_name_sha1_map.find(name);
1788 if(n == m_media_name_sha1_map.end())
1789 errorstream<<"The server sent a file that has not "
1790 <<"been announced."<<std::endl;
1792 m_media_cache.update_sha1(data);
1797 event.type = CE_TEXTURES_UPDATED;
1798 m_client_event_queue.push_back(event);
1800 else if(command == TOCLIENT_TOOLDEF)
1802 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1804 else if(command == TOCLIENT_NODEDEF)
1806 infostream<<"Client: Received node definitions: packet size: "
1807 <<datasize<<std::endl;
1809 // Mesh update thread must be stopped while
1810 // updating content definitions
1811 assert(!m_mesh_update_thread.IsRunning());
1813 // Decompress node definitions
1814 std::string datastring((char*)&data[2], datasize-2);
1815 std::istringstream is(datastring, std::ios_base::binary);
1816 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1817 std::ostringstream tmp_os;
1818 decompressZlib(tmp_is, tmp_os);
1820 // Deserialize node definitions
1821 std::istringstream tmp_is2(tmp_os.str());
1822 m_nodedef->deSerialize(tmp_is2);
1823 m_nodedef_received = true;
1825 else if(command == TOCLIENT_CRAFTITEMDEF)
1827 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1829 else if(command == TOCLIENT_ITEMDEF)
1831 infostream<<"Client: Received item definitions: packet size: "
1832 <<datasize<<std::endl;
1834 // Mesh update thread must be stopped while
1835 // updating content definitions
1836 assert(!m_mesh_update_thread.IsRunning());
1838 // Decompress item definitions
1839 std::string datastring((char*)&data[2], datasize-2);
1840 std::istringstream is(datastring, std::ios_base::binary);
1841 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1842 std::ostringstream tmp_os;
1843 decompressZlib(tmp_is, tmp_os);
1845 // Deserialize node definitions
1846 std::istringstream tmp_is2(tmp_os.str());
1847 m_itemdef->deSerialize(tmp_is2);
1848 m_itemdef_received = true;
1850 else if(command == TOCLIENT_PLAY_SOUND)
1852 std::string datastring((char*)&data[2], datasize-2);
1853 std::istringstream is(datastring, std::ios_base::binary);
1855 s32 server_id = readS32(is);
1856 std::string name = deSerializeString(is);
1857 float gain = readF1000(is);
1858 int type = readU8(is); // 0=local, 1=positional, 2=object
1859 v3f pos = readV3F1000(is);
1860 u16 object_id = readU16(is);
1861 bool loop = readU8(is);
1866 client_id = m_sound->playSound(name, loop, gain);
1868 case 1: // positional
1869 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1872 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1874 pos = cao->getPosition();
1875 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1876 // TODO: Set up sound to move with object
1881 if(client_id != -1){
1882 m_sounds_server_to_client[server_id] = client_id;
1883 m_sounds_client_to_server[client_id] = server_id;
1885 m_sounds_to_objects[client_id] = object_id;
1888 else if(command == TOCLIENT_STOP_SOUND)
1890 std::string datastring((char*)&data[2], datasize-2);
1891 std::istringstream is(datastring, std::ios_base::binary);
1893 s32 server_id = readS32(is);
1894 std::map<s32, int>::iterator i =
1895 m_sounds_server_to_client.find(server_id);
1896 if(i != m_sounds_server_to_client.end()){
1897 int client_id = i->second;
1898 m_sound->stopSound(client_id);
1901 else if(command == TOCLIENT_PRIVILEGES)
1903 std::string datastring((char*)&data[2], datasize-2);
1904 std::istringstream is(datastring, std::ios_base::binary);
1906 m_privileges.clear();
1907 infostream<<"Client: Privileges updated: ";
1908 u16 num_privileges = readU16(is);
1909 for(u16 i=0; i<num_privileges; i++){
1910 std::string priv = deSerializeString(is);
1911 m_privileges.insert(priv);
1912 infostream<<priv<<" ";
1914 infostream<<std::endl;
1916 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1918 std::string datastring((char*)&data[2], datasize-2);
1919 std::istringstream is(datastring, std::ios_base::binary);
1921 // Store formspec in LocalPlayer
1922 Player *player = m_env.getLocalPlayer();
1923 assert(player != NULL);
1924 player->inventory_formspec = deSerializeLongString(is);
1926 else if(command == TOCLIENT_DETACHED_INVENTORY)
1928 std::string datastring((char*)&data[2], datasize-2);
1929 std::istringstream is(datastring, std::ios_base::binary);
1931 std::string name = deSerializeString(is);
1933 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1935 Inventory *inv = NULL;
1936 if(m_detached_inventories.count(name) > 0)
1937 inv = m_detached_inventories[name];
1939 inv = new Inventory(m_itemdef);
1940 m_detached_inventories[name] = inv;
1942 inv->deSerialize(is);
1944 else if(command == TOCLIENT_SHOW_FORMSPEC)
1946 std::string datastring((char*)&data[2], datasize-2);
1947 std::istringstream is(datastring, std::ios_base::binary);
1949 std::string formspec = deSerializeLongString(is);
1950 std::string formname = deSerializeString(is);
1953 event.type = CE_SHOW_FORMSPEC;
1954 // pointer is required as event is a struct only!
1955 // adding a std:string to a struct isn't possible
1956 event.show_formspec.formspec = new std::string(formspec);
1957 event.show_formspec.formname = new std::string(formname);
1958 m_client_event_queue.push_back(event);
1960 else if(command == TOCLIENT_SPAWN_PARTICLE)
1962 std::string datastring((char*)&data[2], datasize-2);
1963 std::istringstream is(datastring, std::ios_base::binary);
1965 v3f pos = readV3F1000(is);
1966 v3f vel = readV3F1000(is);
1967 v3f acc = readV3F1000(is);
1968 float expirationtime = readF1000(is);
1969 float size = readF1000(is);
1970 bool collisiondetection = readU8(is);
1971 std::string texture = deSerializeLongString(is);
1974 event.type = CE_SPAWN_PARTICLE;
1975 event.spawn_particle.pos = new v3f (pos);
1976 event.spawn_particle.vel = new v3f (vel);
1977 event.spawn_particle.acc = new v3f (acc);
1979 event.spawn_particle.expirationtime = expirationtime;
1980 event.spawn_particle.size = size;
1981 event.add_particlespawner.collisiondetection =
1983 event.spawn_particle.texture = new std::string(texture);
1985 m_client_event_queue.push_back(event);
1987 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1989 std::string datastring((char*)&data[2], datasize-2);
1990 std::istringstream is(datastring, std::ios_base::binary);
1992 u16 amount = readU16(is);
1993 float spawntime = readF1000(is);
1994 v3f minpos = readV3F1000(is);
1995 v3f maxpos = readV3F1000(is);
1996 v3f minvel = readV3F1000(is);
1997 v3f maxvel = readV3F1000(is);
1998 v3f minacc = readV3F1000(is);
1999 v3f maxacc = readV3F1000(is);
2000 float minexptime = readF1000(is);
2001 float maxexptime = readF1000(is);
2002 float minsize = readF1000(is);
2003 float maxsize = readF1000(is);
2004 bool collisiondetection = readU8(is);
2005 std::string texture = deSerializeLongString(is);
2006 u32 id = readU32(is);
2009 event.type = CE_ADD_PARTICLESPAWNER;
2010 event.add_particlespawner.amount = amount;
2011 event.add_particlespawner.spawntime = spawntime;
2013 event.add_particlespawner.minpos = new v3f (minpos);
2014 event.add_particlespawner.maxpos = new v3f (maxpos);
2015 event.add_particlespawner.minvel = new v3f (minvel);
2016 event.add_particlespawner.maxvel = new v3f (maxvel);
2017 event.add_particlespawner.minacc = new v3f (minacc);
2018 event.add_particlespawner.maxacc = new v3f (maxacc);
2020 event.add_particlespawner.minexptime = minexptime;
2021 event.add_particlespawner.maxexptime = maxexptime;
2022 event.add_particlespawner.minsize = minsize;
2023 event.add_particlespawner.maxsize = maxsize;
2024 event.add_particlespawner.collisiondetection = collisiondetection;
2025 event.add_particlespawner.texture = new std::string(texture);
2026 event.add_particlespawner.id = id;
2028 m_client_event_queue.push_back(event);
2030 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2032 std::string datastring((char*)&data[2], datasize-2);
2033 std::istringstream is(datastring, std::ios_base::binary);
2035 u32 id = readU16(is);
2038 event.type = CE_DELETE_PARTICLESPAWNER;
2039 event.delete_particlespawner.id = id;
2041 m_client_event_queue.push_back(event);
2043 else if(command == TOCLIENT_HUDADD)
2045 std::string datastring((char *)&data[2], datasize - 2);
2046 std::istringstream is(datastring, std::ios_base::binary);
2048 u32 id = readU32(is);
2049 u8 type = readU8(is);
2050 v2f pos = readV2F1000(is);
2051 std::string name = deSerializeString(is);
2052 v2f scale = readV2F1000(is);
2053 std::string text = deSerializeString(is);
2054 u32 number = readU32(is);
2055 u32 item = readU32(is);
2056 u32 dir = readU32(is);
2057 v2f align = readV2F1000(is);
2060 event.type = CE_HUDADD;
2061 event.hudadd.id = id;
2062 event.hudadd.type = type;
2063 event.hudadd.pos = new v2f(pos);
2064 event.hudadd.name = new std::string(name);
2065 event.hudadd.scale = new v2f(scale);
2066 event.hudadd.text = new std::string(text);
2067 event.hudadd.number = number;
2068 event.hudadd.item = item;
2069 event.hudadd.dir = dir;
2070 event.hudadd.align = new v2f(align);
2071 m_client_event_queue.push_back(event);
2073 else if(command == TOCLIENT_HUDRM)
2075 std::string datastring((char *)&data[2], datasize - 2);
2076 std::istringstream is(datastring, std::ios_base::binary);
2078 u32 id = readU32(is);
2081 event.type = CE_HUDRM;
2082 event.hudrm.id = id;
2083 m_client_event_queue.push_back(event);
2085 else if(command == TOCLIENT_HUDCHANGE)
2091 std::string datastring((char *)&data[2], datasize - 2);
2092 std::istringstream is(datastring, std::ios_base::binary);
2094 u32 id = readU32(is);
2095 u8 stat = (HudElementStat)readU8(is);
2097 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE)
2098 v2fdata = readV2F1000(is);
2099 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2100 sdata = deSerializeString(is);
2102 intdata = readU32(is);
2105 event.type = CE_HUDCHANGE;
2106 event.hudchange.id = id;
2107 event.hudchange.stat = (HudElementStat)stat;
2108 event.hudchange.v2fdata = new v2f(v2fdata);
2109 event.hudchange.sdata = new std::string(sdata);
2110 event.hudchange.data = intdata;
2111 m_client_event_queue.push_back(event);
2115 infostream<<"Client: Ignoring unknown command "
2116 <<command<<std::endl;
2120 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2122 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2123 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2126 void Client::interact(u8 action, const PointedThing& pointed)
2128 if(connectedAndInitialized() == false){
2129 infostream<<"Client::interact() "
2130 "cancelled (not connected)"
2135 std::ostringstream os(std::ios_base::binary);
2141 [5] u32 length of the next item
2142 [9] serialized PointedThing
2144 0: start digging (from undersurface) or use
2145 1: stop digging (all parameters ignored)
2146 2: digging completed
2147 3: place block or item (to abovesurface)
2150 writeU16(os, TOSERVER_INTERACT);
2151 writeU8(os, action);
2152 writeU16(os, getPlayerItem());
2153 std::ostringstream tmp_os(std::ios::binary);
2154 pointed.serialize(tmp_os);
2155 os<<serializeLongString(tmp_os.str());
2157 std::string s = os.str();
2158 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2161 Send(0, data, true);
2164 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2165 const std::map<std::string, std::string> &fields)
2167 std::ostringstream os(std::ios_base::binary);
2169 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2171 os<<serializeString(formname);
2172 writeU16(os, fields.size());
2173 for(std::map<std::string, std::string>::const_iterator
2174 i = fields.begin(); i != fields.end(); i++){
2175 const std::string &name = i->first;
2176 const std::string &value = i->second;
2177 os<<serializeString(name);
2178 os<<serializeLongString(value);
2182 std::string s = os.str();
2183 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2185 Send(0, data, true);
2188 void Client::sendInventoryFields(const std::string &formname,
2189 const std::map<std::string, std::string> &fields)
2191 std::ostringstream os(std::ios_base::binary);
2193 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2194 os<<serializeString(formname);
2195 writeU16(os, fields.size());
2196 for(std::map<std::string, std::string>::const_iterator
2197 i = fields.begin(); i != fields.end(); i++){
2198 const std::string &name = i->first;
2199 const std::string &value = i->second;
2200 os<<serializeString(name);
2201 os<<serializeLongString(value);
2205 std::string s = os.str();
2206 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2208 Send(0, data, true);
2211 void Client::sendInventoryAction(InventoryAction *a)
2213 std::ostringstream os(std::ios_base::binary);
2217 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2218 os.write((char*)buf, 2);
2223 std::string s = os.str();
2224 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2226 Send(0, data, true);
2229 void Client::sendChatMessage(const std::wstring &message)
2231 std::ostringstream os(std::ios_base::binary);
2235 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2236 os.write((char*)buf, 2);
2239 writeU16(buf, message.size());
2240 os.write((char*)buf, 2);
2243 for(u32 i=0; i<message.size(); i++)
2247 os.write((char*)buf, 2);
2251 std::string s = os.str();
2252 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2254 Send(0, data, true);
2257 void Client::sendChangePassword(const std::wstring oldpassword,
2258 const std::wstring newpassword)
2260 Player *player = m_env.getLocalPlayer();
2264 std::string playername = player->getName();
2265 std::string oldpwd = translatePassword(playername, oldpassword);
2266 std::string newpwd = translatePassword(playername, newpassword);
2268 std::ostringstream os(std::ios_base::binary);
2269 u8 buf[2+PASSWORD_SIZE*2];
2271 [0] u16 TOSERVER_PASSWORD
2272 [2] u8[28] old password
2273 [30] u8[28] new password
2276 writeU16(buf, TOSERVER_PASSWORD);
2277 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2279 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2280 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2282 buf[2+PASSWORD_SIZE-1] = 0;
2283 buf[30+PASSWORD_SIZE-1] = 0;
2284 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2287 std::string s = os.str();
2288 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2290 Send(0, data, true);
2294 void Client::sendDamage(u8 damage)
2296 DSTACK(__FUNCTION_NAME);
2297 std::ostringstream os(std::ios_base::binary);
2299 writeU16(os, TOSERVER_DAMAGE);
2300 writeU8(os, damage);
2303 std::string s = os.str();
2304 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2306 Send(0, data, true);
2309 void Client::sendRespawn()
2311 DSTACK(__FUNCTION_NAME);
2312 std::ostringstream os(std::ios_base::binary);
2314 writeU16(os, TOSERVER_RESPAWN);
2317 std::string s = os.str();
2318 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2320 Send(0, data, true);
2323 void Client::sendPlayerPos()
2325 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2327 LocalPlayer *myplayer = m_env.getLocalPlayer();
2328 if(myplayer == NULL)
2331 // Save bandwidth by only updating position when something changed
2332 if(myplayer->last_position == myplayer->getPosition() &&
2333 myplayer->last_speed == myplayer->getSpeed() &&
2334 myplayer->last_pitch == myplayer->getPitch() &&
2335 myplayer->last_yaw == myplayer->getYaw() &&
2336 myplayer->last_keyPressed == myplayer->keyPressed)
2339 myplayer->last_position = myplayer->getPosition();
2340 myplayer->last_speed = myplayer->getSpeed();
2341 myplayer->last_pitch = myplayer->getPitch();
2342 myplayer->last_yaw = myplayer->getYaw();
2343 myplayer->last_keyPressed = myplayer->keyPressed;
2347 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2348 our_peer_id = m_con.GetPeerID();
2351 // Set peer id if not set already
2352 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2353 myplayer->peer_id = our_peer_id;
2354 // Check that an existing peer_id is the same as the connection's
2355 assert(myplayer->peer_id == our_peer_id);
2357 v3f pf = myplayer->getPosition();
2358 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2359 v3f sf = myplayer->getSpeed();
2360 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2361 s32 pitch = myplayer->getPitch() * 100;
2362 s32 yaw = myplayer->getYaw() * 100;
2363 u32 keyPressed=myplayer->keyPressed;
2367 [2] v3s32 position*100
2368 [2+12] v3s32 speed*100
2369 [2+12+12] s32 pitch*100
2370 [2+12+12+4] s32 yaw*100
2371 [2+12+12+4+4] u32 keyPressed
2373 SharedBuffer<u8> data(2+12+12+4+4+4);
2374 writeU16(&data[0], TOSERVER_PLAYERPOS);
2375 writeV3S32(&data[2], position);
2376 writeV3S32(&data[2+12], speed);
2377 writeS32(&data[2+12+12], pitch);
2378 writeS32(&data[2+12+12+4], yaw);
2379 writeU32(&data[2+12+12+4+4], keyPressed);
2380 // Send as unreliable
2381 Send(0, data, false);
2384 void Client::sendPlayerItem(u16 item)
2386 Player *myplayer = m_env.getLocalPlayer();
2387 if(myplayer == NULL)
2390 u16 our_peer_id = m_con.GetPeerID();
2392 // Set peer id if not set already
2393 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2394 myplayer->peer_id = our_peer_id;
2395 // Check that an existing peer_id is the same as the connection's
2396 assert(myplayer->peer_id == our_peer_id);
2398 SharedBuffer<u8> data(2+2);
2399 writeU16(&data[0], TOSERVER_PLAYERITEM);
2400 writeU16(&data[2], item);
2403 Send(0, data, true);
2406 void Client::removeNode(v3s16 p)
2408 std::map<v3s16, MapBlock*> modified_blocks;
2412 //TimeTaker t("removeNodeAndUpdate", m_device);
2413 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2415 catch(InvalidPositionException &e)
2419 // add urgent task to update the modified node
2420 addUpdateMeshTaskForNode(p, false, true);
2422 for(std::map<v3s16, MapBlock * >::iterator
2423 i = modified_blocks.begin();
2424 i != modified_blocks.end(); ++i)
2426 addUpdateMeshTaskWithEdge(i->first);
2430 void Client::addNode(v3s16 p, MapNode n)
2432 TimeTaker timer1("Client::addNode()");
2434 std::map<v3s16, MapBlock*> modified_blocks;
2438 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2439 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2441 catch(InvalidPositionException &e)
2444 for(std::map<v3s16, MapBlock * >::iterator
2445 i = modified_blocks.begin();
2446 i != modified_blocks.end(); ++i)
2448 addUpdateMeshTaskWithEdge(i->first);
2452 void Client::setPlayerControl(PlayerControl &control)
2454 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2455 LocalPlayer *player = m_env.getLocalPlayer();
2456 assert(player != NULL);
2457 player->control = control;
2460 void Client::selectPlayerItem(u16 item)
2462 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2463 m_playeritem = item;
2464 m_inventory_updated = true;
2465 sendPlayerItem(item);
2468 // Returns true if the inventory of the local player has been
2469 // updated from the server. If it is true, it is set to false.
2470 bool Client::getLocalInventoryUpdated()
2472 // m_inventory_updated is behind envlock
2473 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2474 bool updated = m_inventory_updated;
2475 m_inventory_updated = false;
2479 // Copies the inventory of the local player to parameter
2480 void Client::getLocalInventory(Inventory &dst)
2482 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2483 Player *player = m_env.getLocalPlayer();
2484 assert(player != NULL);
2485 dst = player->inventory;
2488 Inventory* Client::getInventory(const InventoryLocation &loc)
2491 case InventoryLocation::UNDEFINED:
2494 case InventoryLocation::CURRENT_PLAYER:
2496 Player *player = m_env.getLocalPlayer();
2497 assert(player != NULL);
2498 return &player->inventory;
2501 case InventoryLocation::PLAYER:
2503 Player *player = m_env.getPlayer(loc.name.c_str());
2506 return &player->inventory;
2509 case InventoryLocation::NODEMETA:
2511 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2514 return meta->getInventory();
2517 case InventoryLocation::DETACHED:
2519 if(m_detached_inventories.count(loc.name) == 0)
2521 return m_detached_inventories[loc.name];
2529 void Client::inventoryAction(InventoryAction *a)
2532 Send it to the server
2534 sendInventoryAction(a);
2537 Predict some local inventory changes
2539 a->clientApply(this, this);
2542 ClientActiveObject * Client::getSelectedActiveObject(
2544 v3f from_pos_f_on_map,
2545 core::line3d<f32> shootline_on_map
2548 std::vector<DistanceSortedActiveObject> objects;
2550 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2552 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2555 // After this, the closest object is the first in the array.
2556 std::sort(objects.begin(), objects.end());
2558 for(u32 i=0; i<objects.size(); i++)
2560 ClientActiveObject *obj = objects[i].obj;
2562 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2563 if(selection_box == NULL)
2566 v3f pos = obj->getPosition();
2568 core::aabbox3d<f32> offsetted_box(
2569 selection_box->MinEdge + pos,
2570 selection_box->MaxEdge + pos
2573 if(offsetted_box.intersectsWithLine(shootline_on_map))
2575 //infostream<<"Returning selected object"<<std::endl;
2580 //infostream<<"No object selected; returning NULL."<<std::endl;
2584 void Client::printDebugInfo(std::ostream &os)
2586 //JMutexAutoLock lock1(m_fetchblock_mutex);
2587 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2589 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2590 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2591 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2595 std::list<std::string> Client::getConnectedPlayerNames()
2597 return m_env.getPlayerNames();
2600 float Client::getAnimationTime()
2602 return m_animation_time;
2605 int Client::getCrackLevel()
2607 return m_crack_level;
2610 void Client::setCrack(int level, v3s16 pos)
2612 int old_crack_level = m_crack_level;
2613 v3s16 old_crack_pos = m_crack_pos;
2615 m_crack_level = level;
2618 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2621 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2623 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2626 addUpdateMeshTaskForNode(pos, false, true);
2632 Player *player = m_env.getLocalPlayer();
2633 assert(player != NULL);
2637 bool Client::getChatMessage(std::wstring &message)
2639 if(m_chat_queue.size() == 0)
2641 message = m_chat_queue.pop_front();
2645 void Client::typeChatMessage(const std::wstring &message)
2647 // Discard empty line
2652 sendChatMessage(message);
2655 if (message[0] == L'/')
2657 m_chat_queue.push_back(
2658 (std::wstring)L"issued command: "+message);
2662 LocalPlayer *player = m_env.getLocalPlayer();
2663 assert(player != NULL);
2664 std::wstring name = narrow_to_wide(player->getName());
2665 m_chat_queue.push_back(
2666 (std::wstring)L"<"+name+L"> "+message);
2670 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2672 /*infostream<<"Client::addUpdateMeshTask(): "
2673 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2674 <<" ack_to_server="<<ack_to_server
2675 <<" urgent="<<urgent
2678 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2683 Create a task to update the mesh of the block
2686 MeshMakeData *data = new MeshMakeData(this);
2689 //TimeTaker timer("data fill");
2691 // Debug: 1-6ms, avg=2ms
2693 data->setCrack(m_crack_level, m_crack_pos);
2694 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2698 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2700 // Add task to queue
2701 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2703 /*infostream<<"Mesh update input queue size is "
2704 <<m_mesh_update_thread.m_queue_in.size()
2708 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2712 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2713 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2718 v3s16 p = blockpos + v3s16(0,0,0);
2719 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2720 addUpdateMeshTask(p, ack_to_server, urgent);
2722 catch(InvalidPositionException &e){}
2725 v3s16 p = blockpos + v3s16(-1,0,0);
2726 addUpdateMeshTask(p, false, urgent);
2728 catch(InvalidPositionException &e){}
2730 v3s16 p = blockpos + v3s16(0,-1,0);
2731 addUpdateMeshTask(p, false, urgent);
2733 catch(InvalidPositionException &e){}
2735 v3s16 p = blockpos + v3s16(0,0,-1);
2736 addUpdateMeshTask(p, false, urgent);
2738 catch(InvalidPositionException &e){}
2741 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2745 infostream<<"Client::addUpdateMeshTaskForNode(): "
2746 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2750 v3s16 blockpos = getNodeBlockPos(nodepos);
2751 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2754 v3s16 p = blockpos + v3s16(0,0,0);
2755 addUpdateMeshTask(p, ack_to_server, urgent);
2757 catch(InvalidPositionException &e){}
2759 if(nodepos.X == blockpos_relative.X){
2761 v3s16 p = blockpos + v3s16(-1,0,0);
2762 addUpdateMeshTask(p, false, urgent);
2764 catch(InvalidPositionException &e){}
2766 if(nodepos.Y == blockpos_relative.Y){
2768 v3s16 p = blockpos + v3s16(0,-1,0);
2769 addUpdateMeshTask(p, false, urgent);
2771 catch(InvalidPositionException &e){}
2773 if(nodepos.Z == blockpos_relative.Z){
2775 v3s16 p = blockpos + v3s16(0,0,-1);
2776 addUpdateMeshTask(p, false, urgent);
2778 catch(InvalidPositionException &e){}
2782 ClientEvent Client::getClientEvent()
2784 if(m_client_event_queue.size() == 0)
2787 event.type = CE_NONE;
2790 return m_client_event_queue.pop_front();
2793 void Client::afterContentReceived()
2795 infostream<<"Client::afterContentReceived() started"<<std::endl;
2796 assert(m_itemdef_received);
2797 assert(m_nodedef_received);
2798 assert(texturesReceived());
2800 // remove the information about which checksum each texture
2802 m_media_name_sha1_map.clear();
2804 // Rebuild inherited images and recreate textures
2805 infostream<<"- Rebuilding images and textures"<<std::endl;
2806 m_tsrc->rebuildImagesAndTextures();
2808 // Update texture atlas
2809 infostream<<"- Updating texture atlas"<<std::endl;
2810 if(g_settings->getBool("enable_texture_atlas"))
2811 m_tsrc->buildMainAtlas(this);
2814 m_shsrc->rebuildShaders();
2816 // Update node aliases
2817 infostream<<"- Updating node aliases"<<std::endl;
2818 m_nodedef->updateAliases(m_itemdef);
2820 // Update node textures
2821 infostream<<"- Updating node textures"<<std::endl;
2822 m_nodedef->updateTextures(m_tsrc);
2824 // Preload item textures and meshes if configured to
2825 if(g_settings->getBool("preload_item_visuals"))
2827 verbosestream<<"Updating item textures and meshes"<<std::endl;
2828 std::set<std::string> names = m_itemdef->getAll();
2829 for(std::set<std::string>::const_iterator
2830 i = names.begin(); i != names.end(); ++i){
2831 // Asking for these caches the result
2832 m_itemdef->getInventoryTexture(*i, this);
2833 m_itemdef->getWieldMesh(*i, this);
2837 // Start mesh update thread after setting up content definitions
2838 infostream<<"- Starting mesh update thread"<<std::endl;
2839 m_mesh_update_thread.Start();
2841 infostream<<"Client::afterContentReceived() done"<<std::endl;
2844 float Client::getRTT(void)
2847 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2848 } catch(con::PeerNotFoundException &e){
2853 // IGameDef interface
2855 IItemDefManager* Client::getItemDefManager()
2859 INodeDefManager* Client::getNodeDefManager()
2863 ICraftDefManager* Client::getCraftDefManager()
2866 //return m_craftdef;
2868 ITextureSource* Client::getTextureSource()
2872 IShaderSource* Client::getShaderSource()
2876 u16 Client::allocateUnknownNodeId(const std::string &name)
2878 errorstream<<"Client::allocateUnknownNodeId(): "
2879 <<"Client cannot allocate node IDs"<<std::endl;
2881 return CONTENT_IGNORE;
2883 ISoundManager* Client::getSoundManager()
2887 MtEventManager* Client::getEventManager()