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);
2058 v2f offset = readV2F1000(is);
2061 event.type = CE_HUDADD;
2062 event.hudadd.id = id;
2063 event.hudadd.type = type;
2064 event.hudadd.pos = new v2f(pos);
2065 event.hudadd.name = new std::string(name);
2066 event.hudadd.scale = new v2f(scale);
2067 event.hudadd.text = new std::string(text);
2068 event.hudadd.number = number;
2069 event.hudadd.item = item;
2070 event.hudadd.dir = dir;
2071 event.hudadd.align = new v2f(align);
2072 event.hudadd.offset = new v2f(offset);
2073 m_client_event_queue.push_back(event);
2075 else if(command == TOCLIENT_HUDRM)
2077 std::string datastring((char *)&data[2], datasize - 2);
2078 std::istringstream is(datastring, std::ios_base::binary);
2080 u32 id = readU32(is);
2083 event.type = CE_HUDRM;
2084 event.hudrm.id = id;
2085 m_client_event_queue.push_back(event);
2087 else if(command == TOCLIENT_HUDCHANGE)
2093 std::string datastring((char *)&data[2], datasize - 2);
2094 std::istringstream is(datastring, std::ios_base::binary);
2096 u32 id = readU32(is);
2097 u8 stat = (HudElementStat)readU8(is);
2099 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE)
2100 v2fdata = readV2F1000(is);
2101 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2102 sdata = deSerializeString(is);
2104 intdata = readU32(is);
2107 event.type = CE_HUDCHANGE;
2108 event.hudchange.id = id;
2109 event.hudchange.stat = (HudElementStat)stat;
2110 event.hudchange.v2fdata = new v2f(v2fdata);
2111 event.hudchange.sdata = new std::string(sdata);
2112 event.hudchange.data = intdata;
2113 m_client_event_queue.push_back(event);
2117 infostream<<"Client: Ignoring unknown command "
2118 <<command<<std::endl;
2122 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2124 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2125 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2128 void Client::interact(u8 action, const PointedThing& pointed)
2130 if(connectedAndInitialized() == false){
2131 infostream<<"Client::interact() "
2132 "cancelled (not connected)"
2137 std::ostringstream os(std::ios_base::binary);
2143 [5] u32 length of the next item
2144 [9] serialized PointedThing
2146 0: start digging (from undersurface) or use
2147 1: stop digging (all parameters ignored)
2148 2: digging completed
2149 3: place block or item (to abovesurface)
2152 writeU16(os, TOSERVER_INTERACT);
2153 writeU8(os, action);
2154 writeU16(os, getPlayerItem());
2155 std::ostringstream tmp_os(std::ios::binary);
2156 pointed.serialize(tmp_os);
2157 os<<serializeLongString(tmp_os.str());
2159 std::string s = os.str();
2160 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2163 Send(0, data, true);
2166 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2167 const std::map<std::string, std::string> &fields)
2169 std::ostringstream os(std::ios_base::binary);
2171 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2173 os<<serializeString(formname);
2174 writeU16(os, fields.size());
2175 for(std::map<std::string, std::string>::const_iterator
2176 i = fields.begin(); i != fields.end(); i++){
2177 const std::string &name = i->first;
2178 const std::string &value = i->second;
2179 os<<serializeString(name);
2180 os<<serializeLongString(value);
2184 std::string s = os.str();
2185 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2187 Send(0, data, true);
2190 void Client::sendInventoryFields(const std::string &formname,
2191 const std::map<std::string, std::string> &fields)
2193 std::ostringstream os(std::ios_base::binary);
2195 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2196 os<<serializeString(formname);
2197 writeU16(os, fields.size());
2198 for(std::map<std::string, std::string>::const_iterator
2199 i = fields.begin(); i != fields.end(); i++){
2200 const std::string &name = i->first;
2201 const std::string &value = i->second;
2202 os<<serializeString(name);
2203 os<<serializeLongString(value);
2207 std::string s = os.str();
2208 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2210 Send(0, data, true);
2213 void Client::sendInventoryAction(InventoryAction *a)
2215 std::ostringstream os(std::ios_base::binary);
2219 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2220 os.write((char*)buf, 2);
2225 std::string s = os.str();
2226 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2228 Send(0, data, true);
2231 void Client::sendChatMessage(const std::wstring &message)
2233 std::ostringstream os(std::ios_base::binary);
2237 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2238 os.write((char*)buf, 2);
2241 writeU16(buf, message.size());
2242 os.write((char*)buf, 2);
2245 for(u32 i=0; i<message.size(); i++)
2249 os.write((char*)buf, 2);
2253 std::string s = os.str();
2254 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2256 Send(0, data, true);
2259 void Client::sendChangePassword(const std::wstring oldpassword,
2260 const std::wstring newpassword)
2262 Player *player = m_env.getLocalPlayer();
2266 std::string playername = player->getName();
2267 std::string oldpwd = translatePassword(playername, oldpassword);
2268 std::string newpwd = translatePassword(playername, newpassword);
2270 std::ostringstream os(std::ios_base::binary);
2271 u8 buf[2+PASSWORD_SIZE*2];
2273 [0] u16 TOSERVER_PASSWORD
2274 [2] u8[28] old password
2275 [30] u8[28] new password
2278 writeU16(buf, TOSERVER_PASSWORD);
2279 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2281 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2282 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2284 buf[2+PASSWORD_SIZE-1] = 0;
2285 buf[30+PASSWORD_SIZE-1] = 0;
2286 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2289 std::string s = os.str();
2290 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2292 Send(0, data, true);
2296 void Client::sendDamage(u8 damage)
2298 DSTACK(__FUNCTION_NAME);
2299 std::ostringstream os(std::ios_base::binary);
2301 writeU16(os, TOSERVER_DAMAGE);
2302 writeU8(os, damage);
2305 std::string s = os.str();
2306 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2308 Send(0, data, true);
2311 void Client::sendRespawn()
2313 DSTACK(__FUNCTION_NAME);
2314 std::ostringstream os(std::ios_base::binary);
2316 writeU16(os, TOSERVER_RESPAWN);
2319 std::string s = os.str();
2320 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2322 Send(0, data, true);
2325 void Client::sendPlayerPos()
2327 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2329 LocalPlayer *myplayer = m_env.getLocalPlayer();
2330 if(myplayer == NULL)
2333 // Save bandwidth by only updating position when something changed
2334 if(myplayer->last_position == myplayer->getPosition() &&
2335 myplayer->last_speed == myplayer->getSpeed() &&
2336 myplayer->last_pitch == myplayer->getPitch() &&
2337 myplayer->last_yaw == myplayer->getYaw() &&
2338 myplayer->last_keyPressed == myplayer->keyPressed)
2341 myplayer->last_position = myplayer->getPosition();
2342 myplayer->last_speed = myplayer->getSpeed();
2343 myplayer->last_pitch = myplayer->getPitch();
2344 myplayer->last_yaw = myplayer->getYaw();
2345 myplayer->last_keyPressed = myplayer->keyPressed;
2349 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2350 our_peer_id = m_con.GetPeerID();
2353 // Set peer id if not set already
2354 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2355 myplayer->peer_id = our_peer_id;
2356 // Check that an existing peer_id is the same as the connection's
2357 assert(myplayer->peer_id == our_peer_id);
2359 v3f pf = myplayer->getPosition();
2360 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2361 v3f sf = myplayer->getSpeed();
2362 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2363 s32 pitch = myplayer->getPitch() * 100;
2364 s32 yaw = myplayer->getYaw() * 100;
2365 u32 keyPressed=myplayer->keyPressed;
2369 [2] v3s32 position*100
2370 [2+12] v3s32 speed*100
2371 [2+12+12] s32 pitch*100
2372 [2+12+12+4] s32 yaw*100
2373 [2+12+12+4+4] u32 keyPressed
2375 SharedBuffer<u8> data(2+12+12+4+4+4);
2376 writeU16(&data[0], TOSERVER_PLAYERPOS);
2377 writeV3S32(&data[2], position);
2378 writeV3S32(&data[2+12], speed);
2379 writeS32(&data[2+12+12], pitch);
2380 writeS32(&data[2+12+12+4], yaw);
2381 writeU32(&data[2+12+12+4+4], keyPressed);
2382 // Send as unreliable
2383 Send(0, data, false);
2386 void Client::sendPlayerItem(u16 item)
2388 Player *myplayer = m_env.getLocalPlayer();
2389 if(myplayer == NULL)
2392 u16 our_peer_id = m_con.GetPeerID();
2394 // Set peer id if not set already
2395 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2396 myplayer->peer_id = our_peer_id;
2397 // Check that an existing peer_id is the same as the connection's
2398 assert(myplayer->peer_id == our_peer_id);
2400 SharedBuffer<u8> data(2+2);
2401 writeU16(&data[0], TOSERVER_PLAYERITEM);
2402 writeU16(&data[2], item);
2405 Send(0, data, true);
2408 void Client::removeNode(v3s16 p)
2410 std::map<v3s16, MapBlock*> modified_blocks;
2414 //TimeTaker t("removeNodeAndUpdate", m_device);
2415 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2417 catch(InvalidPositionException &e)
2421 // add urgent task to update the modified node
2422 addUpdateMeshTaskForNode(p, false, true);
2424 for(std::map<v3s16, MapBlock * >::iterator
2425 i = modified_blocks.begin();
2426 i != modified_blocks.end(); ++i)
2428 addUpdateMeshTaskWithEdge(i->first);
2432 void Client::addNode(v3s16 p, MapNode n)
2434 TimeTaker timer1("Client::addNode()");
2436 std::map<v3s16, MapBlock*> modified_blocks;
2440 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2441 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2443 catch(InvalidPositionException &e)
2446 for(std::map<v3s16, MapBlock * >::iterator
2447 i = modified_blocks.begin();
2448 i != modified_blocks.end(); ++i)
2450 addUpdateMeshTaskWithEdge(i->first);
2454 void Client::setPlayerControl(PlayerControl &control)
2456 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2457 LocalPlayer *player = m_env.getLocalPlayer();
2458 assert(player != NULL);
2459 player->control = control;
2462 void Client::selectPlayerItem(u16 item)
2464 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2465 m_playeritem = item;
2466 m_inventory_updated = true;
2467 sendPlayerItem(item);
2470 // Returns true if the inventory of the local player has been
2471 // updated from the server. If it is true, it is set to false.
2472 bool Client::getLocalInventoryUpdated()
2474 // m_inventory_updated is behind envlock
2475 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2476 bool updated = m_inventory_updated;
2477 m_inventory_updated = false;
2481 // Copies the inventory of the local player to parameter
2482 void Client::getLocalInventory(Inventory &dst)
2484 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2485 Player *player = m_env.getLocalPlayer();
2486 assert(player != NULL);
2487 dst = player->inventory;
2490 Inventory* Client::getInventory(const InventoryLocation &loc)
2493 case InventoryLocation::UNDEFINED:
2496 case InventoryLocation::CURRENT_PLAYER:
2498 Player *player = m_env.getLocalPlayer();
2499 assert(player != NULL);
2500 return &player->inventory;
2503 case InventoryLocation::PLAYER:
2505 Player *player = m_env.getPlayer(loc.name.c_str());
2508 return &player->inventory;
2511 case InventoryLocation::NODEMETA:
2513 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2516 return meta->getInventory();
2519 case InventoryLocation::DETACHED:
2521 if(m_detached_inventories.count(loc.name) == 0)
2523 return m_detached_inventories[loc.name];
2531 void Client::inventoryAction(InventoryAction *a)
2534 Send it to the server
2536 sendInventoryAction(a);
2539 Predict some local inventory changes
2541 a->clientApply(this, this);
2544 ClientActiveObject * Client::getSelectedActiveObject(
2546 v3f from_pos_f_on_map,
2547 core::line3d<f32> shootline_on_map
2550 std::vector<DistanceSortedActiveObject> objects;
2552 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2554 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2557 // After this, the closest object is the first in the array.
2558 std::sort(objects.begin(), objects.end());
2560 for(u32 i=0; i<objects.size(); i++)
2562 ClientActiveObject *obj = objects[i].obj;
2564 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2565 if(selection_box == NULL)
2568 v3f pos = obj->getPosition();
2570 core::aabbox3d<f32> offsetted_box(
2571 selection_box->MinEdge + pos,
2572 selection_box->MaxEdge + pos
2575 if(offsetted_box.intersectsWithLine(shootline_on_map))
2577 //infostream<<"Returning selected object"<<std::endl;
2582 //infostream<<"No object selected; returning NULL."<<std::endl;
2586 void Client::printDebugInfo(std::ostream &os)
2588 //JMutexAutoLock lock1(m_fetchblock_mutex);
2589 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2591 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2592 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2593 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2597 std::list<std::string> Client::getConnectedPlayerNames()
2599 return m_env.getPlayerNames();
2602 float Client::getAnimationTime()
2604 return m_animation_time;
2607 int Client::getCrackLevel()
2609 return m_crack_level;
2612 void Client::setCrack(int level, v3s16 pos)
2614 int old_crack_level = m_crack_level;
2615 v3s16 old_crack_pos = m_crack_pos;
2617 m_crack_level = level;
2620 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2623 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2625 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2628 addUpdateMeshTaskForNode(pos, false, true);
2634 Player *player = m_env.getLocalPlayer();
2635 assert(player != NULL);
2639 bool Client::getChatMessage(std::wstring &message)
2641 if(m_chat_queue.size() == 0)
2643 message = m_chat_queue.pop_front();
2647 void Client::typeChatMessage(const std::wstring &message)
2649 // Discard empty line
2654 sendChatMessage(message);
2657 if (message[0] == L'/')
2659 m_chat_queue.push_back(
2660 (std::wstring)L"issued command: "+message);
2664 LocalPlayer *player = m_env.getLocalPlayer();
2665 assert(player != NULL);
2666 std::wstring name = narrow_to_wide(player->getName());
2667 m_chat_queue.push_back(
2668 (std::wstring)L"<"+name+L"> "+message);
2672 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2674 /*infostream<<"Client::addUpdateMeshTask(): "
2675 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2676 <<" ack_to_server="<<ack_to_server
2677 <<" urgent="<<urgent
2680 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2685 Create a task to update the mesh of the block
2688 MeshMakeData *data = new MeshMakeData(this);
2691 //TimeTaker timer("data fill");
2693 // Debug: 1-6ms, avg=2ms
2695 data->setCrack(m_crack_level, m_crack_pos);
2696 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2700 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2702 // Add task to queue
2703 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2705 /*infostream<<"Mesh update input queue size is "
2706 <<m_mesh_update_thread.m_queue_in.size()
2710 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2714 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2715 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2720 v3s16 p = blockpos + v3s16(0,0,0);
2721 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2722 addUpdateMeshTask(p, ack_to_server, urgent);
2724 catch(InvalidPositionException &e){}
2727 v3s16 p = blockpos + v3s16(-1,0,0);
2728 addUpdateMeshTask(p, false, urgent);
2730 catch(InvalidPositionException &e){}
2732 v3s16 p = blockpos + v3s16(0,-1,0);
2733 addUpdateMeshTask(p, false, urgent);
2735 catch(InvalidPositionException &e){}
2737 v3s16 p = blockpos + v3s16(0,0,-1);
2738 addUpdateMeshTask(p, false, urgent);
2740 catch(InvalidPositionException &e){}
2743 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2747 infostream<<"Client::addUpdateMeshTaskForNode(): "
2748 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2752 v3s16 blockpos = getNodeBlockPos(nodepos);
2753 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2756 v3s16 p = blockpos + v3s16(0,0,0);
2757 addUpdateMeshTask(p, ack_to_server, urgent);
2759 catch(InvalidPositionException &e){}
2761 if(nodepos.X == blockpos_relative.X){
2763 v3s16 p = blockpos + v3s16(-1,0,0);
2764 addUpdateMeshTask(p, false, urgent);
2766 catch(InvalidPositionException &e){}
2768 if(nodepos.Y == blockpos_relative.Y){
2770 v3s16 p = blockpos + v3s16(0,-1,0);
2771 addUpdateMeshTask(p, false, urgent);
2773 catch(InvalidPositionException &e){}
2775 if(nodepos.Z == blockpos_relative.Z){
2777 v3s16 p = blockpos + v3s16(0,0,-1);
2778 addUpdateMeshTask(p, false, urgent);
2780 catch(InvalidPositionException &e){}
2784 ClientEvent Client::getClientEvent()
2786 if(m_client_event_queue.size() == 0)
2789 event.type = CE_NONE;
2792 return m_client_event_queue.pop_front();
2795 void Client::afterContentReceived()
2797 infostream<<"Client::afterContentReceived() started"<<std::endl;
2798 assert(m_itemdef_received);
2799 assert(m_nodedef_received);
2800 assert(texturesReceived());
2802 // remove the information about which checksum each texture
2804 m_media_name_sha1_map.clear();
2806 // Rebuild inherited images and recreate textures
2807 infostream<<"- Rebuilding images and textures"<<std::endl;
2808 m_tsrc->rebuildImagesAndTextures();
2810 // Update texture atlas
2811 infostream<<"- Updating texture atlas"<<std::endl;
2812 if(g_settings->getBool("enable_texture_atlas"))
2813 m_tsrc->buildMainAtlas(this);
2816 m_shsrc->rebuildShaders();
2818 // Update node aliases
2819 infostream<<"- Updating node aliases"<<std::endl;
2820 m_nodedef->updateAliases(m_itemdef);
2822 // Update node textures
2823 infostream<<"- Updating node textures"<<std::endl;
2824 m_nodedef->updateTextures(m_tsrc);
2826 // Preload item textures and meshes if configured to
2827 if(g_settings->getBool("preload_item_visuals"))
2829 verbosestream<<"Updating item textures and meshes"<<std::endl;
2830 std::set<std::string> names = m_itemdef->getAll();
2831 for(std::set<std::string>::const_iterator
2832 i = names.begin(); i != names.end(); ++i){
2833 // Asking for these caches the result
2834 m_itemdef->getInventoryTexture(*i, this);
2835 m_itemdef->getWieldMesh(*i, this);
2839 // Start mesh update thread after setting up content definitions
2840 infostream<<"- Starting mesh update thread"<<std::endl;
2841 m_mesh_update_thread.Start();
2843 infostream<<"Client::afterContentReceived() done"<<std::endl;
2846 float Client::getRTT(void)
2849 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2850 } catch(con::PeerNotFoundException &e){
2855 // IGameDef interface
2857 IItemDefManager* Client::getItemDefManager()
2861 INodeDefManager* Client::getNodeDefManager()
2865 ICraftDefManager* Client::getCraftDefManager()
2868 //return m_craftdef;
2870 ITextureSource* Client::getTextureSource()
2874 IShaderSource* Client::getShaderSource()
2878 u16 Client::allocateUnknownNodeId(const std::string &name)
2880 errorstream<<"Client::allocateUnknownNodeId(): "
2881 <<"Client cannot allocate node IDs"<<std::endl;
2883 return CONTENT_IGNORE;
2885 ISoundManager* Client::getSoundManager()
2889 MtEventManager* Client::getEventManager()