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"
34 #include "nodemetadata.h"
38 #include <IFileSystem.h>
41 #include "clientmap.h"
42 #include "filecache.h"
44 #include "util/string.h"
46 #include "IMeshCache.h"
47 #include "util/serialize.h"
49 #include "util/directiontables.h"
52 #include <curl/curl.h>
55 static std::string getMediaCacheDir()
57 return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media";
64 QueuedMeshUpdate::QueuedMeshUpdate():
67 ack_block_to_server(false)
71 QueuedMeshUpdate::~QueuedMeshUpdate()
81 MeshUpdateQueue::MeshUpdateQueue()
86 MeshUpdateQueue::~MeshUpdateQueue()
88 JMutexAutoLock lock(m_mutex);
90 for(std::vector<QueuedMeshUpdate*>::iterator
92 i != m_queue.end(); i++)
94 QueuedMeshUpdate *q = *i;
100 peer_id=0 adds with nobody to send to
102 void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
104 DSTACK(__FUNCTION_NAME);
108 JMutexAutoLock lock(m_mutex);
114 Find if block is already in queue.
115 If it is, update the data and quit.
117 for(std::vector<QueuedMeshUpdate*>::iterator
119 i != m_queue.end(); i++)
121 QueuedMeshUpdate *q = *i;
127 if(ack_block_to_server)
128 q->ack_block_to_server = true;
136 QueuedMeshUpdate *q = new QueuedMeshUpdate;
139 q->ack_block_to_server = ack_block_to_server;
140 m_queue.push_back(q);
143 // Returned pointer must be deleted
144 // Returns NULL if queue is empty
145 QueuedMeshUpdate * MeshUpdateQueue::pop()
147 JMutexAutoLock lock(m_mutex);
149 bool must_be_urgent = !m_urgents.empty();
150 for(std::vector<QueuedMeshUpdate*>::iterator
152 i != m_queue.end(); i++)
154 QueuedMeshUpdate *q = *i;
155 if(must_be_urgent && m_urgents.count(q->p) == 0)
158 m_urgents.erase(q->p);
168 void * MeshUpdateThread::Thread()
172 log_register_thread("MeshUpdateThread");
174 DSTACK(__FUNCTION_NAME);
176 BEGIN_DEBUG_EXCEPTION_HANDLER
180 /*// Wait for output queue to flush.
181 // Allow 2 in queue, this makes less frametime jitter.
182 // Umm actually, there is no much difference
183 if(m_queue_out.size() >= 2)
189 QueuedMeshUpdate *q = m_queue_in.pop();
196 ScopeProfiler sp(g_profiler, "Client: Mesh making");
198 MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
199 if(mesh_new->getMesh()->getMeshBufferCount() == 0)
208 r.ack_block_to_server = q->ack_block_to_server;
210 /*infostream<<"MeshUpdateThread: Processed "
211 <<"("<<q->p.X<<","<<q->p.Y<<","<<q->p.Z<<")"
214 m_queue_out.push_back(r);
219 END_DEBUG_EXCEPTION_HANDLER(errorstream)
224 void * MediaFetchThread::Thread()
228 log_register_thread("MediaFetchThread");
230 DSTACK(__FUNCTION_NAME);
232 BEGIN_DEBUG_EXCEPTION_HANDLER
237 for (std::list<MediaRequest>::iterator i = m_file_requests.begin();
238 i != m_file_requests.end(); ++i) {
239 curl = curl_easy_init();
241 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
242 curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str());
243 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
244 std::ostringstream stream;
245 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
246 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
247 res = curl_easy_perform(curl);
248 if (res == CURLE_OK) {
249 std::string data = stream.str();
250 m_file_data.push_back(make_pair(i->name, data));
252 m_failed.push_back(*i);
253 infostream << "cURL request failed for " << i->name << std::endl;
255 curl_easy_cleanup(curl);
259 END_DEBUG_EXCEPTION_HANDLER(errorstream)
265 IrrlichtDevice *device,
266 const char *playername,
267 std::string password,
268 MapDrawControl &control,
269 IWritableTextureSource *tsrc,
270 IWritableShaderSource *shsrc,
271 IWritableItemDefManager *itemdef,
272 IWritableNodeDefManager *nodedef,
273 ISoundManager *sound,
274 MtEventManager *event,
283 m_mesh_update_thread(this),
285 new ClientMap(this, this, control,
286 device->getSceneManager()->getRootSceneNode(),
287 device->getSceneManager(), 666),
288 device->getSceneManager(),
291 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
293 m_server_ser_ver(SER_FMT_VER_INVALID),
295 m_inventory_updated(false),
296 m_inventory_from_server(NULL),
297 m_inventory_from_server_age(0.0),
302 m_password(password),
303 m_access_denied(false),
304 m_media_cache(getMediaCacheDir()),
305 m_media_receive_started(false),
307 m_media_received_count(0),
308 m_itemdef_received(false),
309 m_nodedef_received(false),
310 m_time_of_day_set(false),
311 m_last_time_of_day_f(-1),
312 m_time_of_day_update_timer(0),
313 m_recommended_send_interval(0.1),
314 m_removed_sounds_check_timer(0)
316 m_packetcounter_timer = 0.0;
317 //m_delete_unused_sectors_timer = 0.0;
318 m_connection_reinit_timer = 0.0;
319 m_avg_rtt_timer = 0.0;
320 m_playerpos_send_timer = 0.0;
321 m_ignore_damage_timer = 0.0;
323 // Build main texture atlas, now that the GameDef exists (that is, us)
324 if(g_settings->getBool("enable_texture_atlas"))
325 m_tsrc->buildMainAtlas(this);
327 infostream<<"Not building texture atlas."<<std::endl;
333 Player *player = new LocalPlayer(this);
335 player->updateName(playername);
337 m_env.addPlayer(player);
340 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
341 m_media_fetch_threads.push_back(new MediaFetchThread(this));
347 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
351 m_mesh_update_thread.setRun(false);
352 while(m_mesh_update_thread.IsRunning())
354 while(!m_mesh_update_thread.m_queue_out.empty()) {
355 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
360 delete m_inventory_from_server;
362 // Delete detached inventories
364 for(std::map<std::string, Inventory*>::iterator
365 i = m_detached_inventories.begin();
366 i != m_detached_inventories.end(); i++){
371 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
372 i != m_media_fetch_threads.end(); ++i)
375 // cleanup 3d model meshes on client shutdown
376 while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
377 scene::IAnimatedMesh * mesh =
378 m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
381 m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
385 void Client::connect(Address address)
387 DSTACK(__FUNCTION_NAME);
388 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
389 m_con.SetTimeoutMs(0);
390 m_con.Connect(address);
393 bool Client::connectedAndInitialized()
395 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
397 if(m_con.Connected() == false)
400 if(m_server_ser_ver == SER_FMT_VER_INVALID)
406 void Client::step(float dtime)
408 DSTACK(__FUNCTION_NAME);
414 if(m_ignore_damage_timer > dtime)
415 m_ignore_damage_timer -= dtime;
417 m_ignore_damage_timer = 0.0;
419 m_animation_time += dtime;
420 if(m_animation_time > 60.0)
421 m_animation_time -= 60.0;
423 m_time_of_day_update_timer += dtime;
425 //infostream<<"Client steps "<<dtime<<std::endl;
428 //TimeTaker timer("ReceiveAll()", m_device);
434 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
436 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
437 m_con.RunTimeouts(dtime);
444 float &counter = m_packetcounter_timer;
450 infostream<<"Client packetcounter (20s):"<<std::endl;
451 m_packetcounter.print(infostream);
452 m_packetcounter.clear();
456 // Get connection status
457 bool connected = connectedAndInitialized();
462 Delete unused sectors
464 NOTE: This jams the game for a while because deleting sectors
468 float &counter = m_delete_unused_sectors_timer;
476 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
478 core::list<v3s16> deleted_blocks;
480 float delete_unused_sectors_timeout =
481 g_settings->getFloat("client_delete_unused_sectors_timeout");
483 // Delete sector blocks
484 /*u32 num = m_env.getMap().unloadUnusedData
485 (delete_unused_sectors_timeout,
486 true, &deleted_blocks);*/
488 // Delete whole sectors
489 m_env.getMap().unloadUnusedData
490 (delete_unused_sectors_timeout,
493 if(deleted_blocks.size() > 0)
495 /*infostream<<"Client: Deleted blocks of "<<num
496 <<" unused sectors"<<std::endl;*/
497 /*infostream<<"Client: Deleted "<<num
498 <<" unused sectors"<<std::endl;*/
504 // Env is locked so con can be locked.
505 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
507 core::list<v3s16>::Iterator i = deleted_blocks.begin();
508 core::list<v3s16> sendlist;
511 if(sendlist.size() == 255 || i == deleted_blocks.end())
513 if(sendlist.size() == 0)
522 u32 replysize = 2+1+6*sendlist.size();
523 SharedBuffer<u8> reply(replysize);
524 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
525 reply[2] = sendlist.size();
527 for(core::list<v3s16>::Iterator
528 j = sendlist.begin();
529 j != sendlist.end(); j++)
531 writeV3S16(&reply[2+1+6*k], *j);
534 m_con.Send(PEER_ID_SERVER, 1, reply, true);
536 if(i == deleted_blocks.end())
542 sendlist.push_back(*i);
550 if(connected == false)
552 float &counter = m_connection_reinit_timer;
558 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
560 Player *myplayer = m_env.getLocalPlayer();
561 assert(myplayer != NULL);
563 // Send TOSERVER_INIT
564 // [0] u16 TOSERVER_INIT
565 // [2] u8 SER_FMT_VER_HIGHEST
566 // [3] u8[20] player_name
567 // [23] u8[28] password (new in some version)
568 // [51] u16 minimum supported network protocol version (added sometime)
569 // [53] u16 maximum supported network protocol version (added later than the previous one)
570 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
571 writeU16(&data[0], TOSERVER_INIT);
572 writeU8(&data[2], SER_FMT_VER_HIGHEST);
574 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
575 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
577 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
580 memset((char*)&data[23], 0, PASSWORD_SIZE);
581 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
583 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
584 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
586 // Send as unreliable
587 Send(0, data, false);
590 // Not connected, return
595 Do stuff if connected
599 Run Map's timers and unload unused data
601 const float map_timer_and_unload_dtime = 5.25;
602 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
604 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
605 std::list<v3s16> deleted_blocks;
606 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
607 g_settings->getFloat("client_unload_unused_data_timeout"),
610 /*if(deleted_blocks.size() > 0)
611 infostream<<"Client: Unloaded "<<deleted_blocks.size()
612 <<" unused blocks"<<std::endl;*/
616 NOTE: This loop is intentionally iterated the way it is.
619 std::list<v3s16>::iterator i = deleted_blocks.begin();
620 std::list<v3s16> sendlist;
623 if(sendlist.size() == 255 || i == deleted_blocks.end())
625 if(sendlist.size() == 0)
634 u32 replysize = 2+1+6*sendlist.size();
635 SharedBuffer<u8> reply(replysize);
636 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
637 reply[2] = sendlist.size();
639 for(std::list<v3s16>::iterator
640 j = sendlist.begin();
641 j != sendlist.end(); ++j)
643 writeV3S16(&reply[2+1+6*k], *j);
646 m_con.Send(PEER_ID_SERVER, 1, reply, true);
648 if(i == deleted_blocks.end())
654 sendlist.push_back(*i);
664 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
666 // Control local player (0ms)
667 LocalPlayer *player = m_env.getLocalPlayer();
668 assert(player != NULL);
669 player->applyControl(dtime);
671 //TimeTaker envtimer("env step", m_device);
680 ClientEnvEvent event = m_env.getClientEvent();
681 if(event.type == CEE_NONE)
685 else if(event.type == CEE_PLAYER_DAMAGE)
687 if(m_ignore_damage_timer <= 0)
689 u8 damage = event.player_damage.amount;
691 if(event.player_damage.send_to_server)
694 // Add to ClientEvent queue
696 event.type = CE_PLAYER_DAMAGE;
697 event.player_damage.amount = damage;
698 m_client_event_queue.push_back(event);
708 float &counter = m_avg_rtt_timer;
713 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
714 // connectedAndInitialized() is true, peer exists.
715 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
716 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
721 Send player position to server
724 float &counter = m_playerpos_send_timer;
726 if(counter >= m_recommended_send_interval)
734 Replace updated meshes
737 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
739 //TimeTaker timer("** Processing mesh update result queue");
742 /*infostream<<"Mesh update result queue size is "
743 <<m_mesh_update_thread.m_queue_out.size()
746 int num_processed_meshes = 0;
747 while(!m_mesh_update_thread.m_queue_out.empty())
749 num_processed_meshes++;
750 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
751 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
754 //JMutexAutoLock lock(block->mesh_mutex);
756 // Delete the old mesh
757 if(block->mesh != NULL)
759 // TODO: Remove hardware buffers of meshbuffers of block->mesh
764 // Replace with the new mesh
765 block->mesh = r.mesh;
769 if(r.ack_block_to_server)
771 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
772 <<","<<r.p.Z<<")"<<std::endl;*/
783 u32 replysize = 2+1+6;
784 SharedBuffer<u8> reply(replysize);
785 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
787 writeV3S16(&reply[3], r.p);
789 m_con.Send(PEER_ID_SERVER, 1, reply, true);
792 if(num_processed_meshes > 0)
793 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
799 if (m_media_receive_started) {
800 bool all_stopped = true;
801 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
802 thread != m_media_fetch_threads.end(); ++thread) {
803 all_stopped &= !(*thread)->IsRunning();
804 while (!(*thread)->m_file_data.empty()) {
805 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
806 ++m_media_received_count;
808 bool success = loadMedia(out.second, out.first);
810 verbosestream<<"Client: Loaded received media: "
811 <<"\""<<out.first<<"\". Caching."<<std::endl;
813 infostream<<"Client: Failed to load received media: "
814 <<"\""<<out.first<<"\". Not caching."<<std::endl;
818 bool did = fs::CreateAllDirs(getMediaCacheDir());
820 errorstream<<"Could not create media cache directory"
825 std::map<std::string, std::string>::iterator n;
826 n = m_media_name_sha1_map.find(out.first);
827 if(n == m_media_name_sha1_map.end())
828 errorstream<<"The server sent a file that has not "
829 <<"been announced."<<std::endl;
831 m_media_cache.update_sha1(out.second);
836 std::list<MediaRequest> fetch_failed;
837 for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
838 thread != m_media_fetch_threads.end(); ++thread) {
839 for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
840 request != (*thread)->m_failed.end(); ++request)
841 fetch_failed.push_back(*request);
842 (*thread)->m_failed.clear();
844 if (fetch_failed.size() > 0) {
845 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
846 << "Requesting them the usual way." << std::endl;
847 request_media(fetch_failed);
853 If the server didn't update the inventory in a while, revert
854 the local inventory (so the player notices the lag problem
855 and knows something is wrong).
857 if(m_inventory_from_server)
859 float interval = 10.0;
860 float count_before = floor(m_inventory_from_server_age / interval);
862 m_inventory_from_server_age += dtime;
864 float count_after = floor(m_inventory_from_server_age / interval);
866 if(count_after != count_before)
868 // Do this every <interval> seconds after TOCLIENT_INVENTORY
869 // Reset the locally changed inventory to the authoritative inventory
870 Player *player = m_env.getLocalPlayer();
871 player->inventory = *m_inventory_from_server;
872 m_inventory_updated = true;
877 Update positions of sounds attached to objects
880 for(std::map<int, u16>::iterator
881 i = m_sounds_to_objects.begin();
882 i != m_sounds_to_objects.end(); i++)
884 int client_id = i->first;
885 u16 object_id = i->second;
886 ClientActiveObject *cao = m_env.getActiveObject(object_id);
889 v3f pos = cao->getPosition();
890 m_sound->updateSoundPosition(client_id, pos);
895 Handle removed remotely initiated sounds
897 m_removed_sounds_check_timer += dtime;
898 if(m_removed_sounds_check_timer >= 2.32)
900 m_removed_sounds_check_timer = 0;
901 // Find removed sounds and clear references to them
902 std::set<s32> removed_server_ids;
903 for(std::map<s32, int>::iterator
904 i = m_sounds_server_to_client.begin();
905 i != m_sounds_server_to_client.end();)
907 s32 server_id = i->first;
908 int client_id = i->second;
910 if(!m_sound->soundExists(client_id)){
911 m_sounds_server_to_client.erase(server_id);
912 m_sounds_client_to_server.erase(client_id);
913 m_sounds_to_objects.erase(client_id);
914 removed_server_ids.insert(server_id);
918 if(removed_server_ids.size() != 0)
920 std::ostringstream os(std::ios_base::binary);
921 writeU16(os, TOSERVER_REMOVED_SOUNDS);
922 writeU16(os, removed_server_ids.size());
923 for(std::set<s32>::iterator i = removed_server_ids.begin();
924 i != removed_server_ids.end(); i++)
926 std::string s = os.str();
927 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
934 bool Client::loadMedia(const std::string &data, const std::string &filename)
936 // Silly irrlicht's const-incorrectness
937 Buffer<char> data_rw(data.c_str(), data.size());
941 const char *image_ext[] = {
942 ".png", ".jpg", ".bmp", ".tga",
943 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
946 name = removeStringEnd(filename, image_ext);
949 verbosestream<<"Client: Attempting to load image "
950 <<"file \""<<filename<<"\""<<std::endl;
952 io::IFileSystem *irrfs = m_device->getFileSystem();
953 video::IVideoDriver *vdrv = m_device->getVideoDriver();
955 // Create an irrlicht memory file
956 io::IReadFile *rfile = irrfs->createMemoryReadFile(
957 *data_rw, data_rw.getSize(), "_tempreadfile");
960 video::IImage *img = vdrv->createImageFromFile(rfile);
962 errorstream<<"Client: Cannot create image from data of "
963 <<"file \""<<filename<<"\""<<std::endl;
968 m_tsrc->insertSourceImage(filename, img);
975 const char *sound_ext[] = {
976 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
977 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
980 name = removeStringEnd(filename, sound_ext);
983 verbosestream<<"Client: Attempting to load sound "
984 <<"file \""<<filename<<"\""<<std::endl;
985 m_sound->loadSoundData(name, data);
989 const char *model_ext[] = {
990 ".x", ".b3d", ".md2", ".obj",
993 name = removeStringEnd(filename, model_ext);
996 verbosestream<<"Client: Storing model into Irrlicht: "
997 <<"\""<<filename<<"\""<<std::endl;
998 scene::ISceneManager *smgr = m_device->getSceneManager();
1000 //check if mesh was already cached
1001 scene::IAnimatedMesh *mesh =
1002 smgr->getMeshCache()->getMeshByName(filename.c_str());
1005 errorstream << "Multiple models with name: " << filename.c_str() <<
1006 " found replacing previous model!" << std::endl;
1008 smgr->getMeshCache()->removeMesh(mesh);
1012 io::IFileSystem *irrfs = m_device->getFileSystem();
1013 io::IReadFile *rfile = irrfs->createMemoryReadFile(
1014 *data_rw, data_rw.getSize(), filename.c_str());
1017 mesh = smgr->getMesh(rfile);
1018 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
1023 errorstream<<"Client: Don't know how to load file \""
1024 <<filename<<"\""<<std::endl;
1028 // Virtual methods from con::PeerHandler
1029 void Client::peerAdded(con::Peer *peer)
1031 infostream<<"Client::peerAdded(): peer->id="
1032 <<peer->id<<std::endl;
1034 void Client::deletingPeer(con::Peer *peer, bool timeout)
1036 infostream<<"Client::deletingPeer(): "
1037 "Server Peer is getting deleted "
1038 <<"(timeout="<<timeout<<")"<<std::endl;
1043 u16 number of files requested
1049 void Client::request_media(const std::list<MediaRequest> &file_requests)
1051 std::ostringstream os(std::ios_base::binary);
1052 writeU16(os, TOSERVER_REQUEST_MEDIA);
1053 writeU16(os, file_requests.size());
1055 for(std::list<MediaRequest>::const_iterator i = file_requests.begin();
1056 i != file_requests.end(); ++i) {
1057 os<<serializeString(i->name);
1061 std::string s = os.str();
1062 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1064 Send(0, data, true);
1065 infostream<<"Client: Sending media request list to server ("
1066 <<file_requests.size()<<" files)"<<std::endl;
1069 void Client::ReceiveAll()
1071 DSTACK(__FUNCTION_NAME);
1072 u32 start_ms = porting::getTimeMs();
1075 // Limit time even if there would be huge amounts of data to
1077 if(porting::getTimeMs() > start_ms + 100)
1082 g_profiler->graphAdd("client_received_packets", 1);
1084 catch(con::NoIncomingDataException &e)
1088 catch(con::InvalidIncomingDataException &e)
1090 infostream<<"Client::ReceiveAll(): "
1091 "InvalidIncomingDataException: what()="
1092 <<e.what()<<std::endl;
1097 void Client::Receive()
1099 DSTACK(__FUNCTION_NAME);
1100 SharedBuffer<u8> data;
1104 //TimeTaker t1("con mutex and receive", m_device);
1105 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1106 datasize = m_con.Receive(sender_peer_id, data);
1108 //TimeTaker t1("ProcessData", m_device);
1109 ProcessData(*data, datasize, sender_peer_id);
1113 sender_peer_id given to this shall be quaranteed to be a valid peer
1115 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1117 DSTACK(__FUNCTION_NAME);
1119 // Ignore packets that don't even fit a command
1122 m_packetcounter.add(60000);
1126 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1128 //infostream<<"Client: received command="<<command<<std::endl;
1129 m_packetcounter.add((u16)command);
1132 If this check is removed, be sure to change the queue
1133 system to know the ids
1135 if(sender_peer_id != PEER_ID_SERVER)
1137 infostream<<"Client::ProcessData(): Discarding data not "
1138 "coming from server: peer_id="<<sender_peer_id
1143 u8 ser_version = m_server_ser_ver;
1145 //infostream<<"Client received command="<<(int)command<<std::endl;
1147 if(command == TOCLIENT_INIT)
1152 u8 deployed = data[2];
1154 infostream<<"Client: TOCLIENT_INIT received with "
1155 "deployed="<<((int)deployed&0xff)<<std::endl;
1157 if(deployed < SER_FMT_VER_LOWEST
1158 || deployed > SER_FMT_VER_HIGHEST)
1160 infostream<<"Client: TOCLIENT_INIT: Server sent "
1161 <<"unsupported ser_fmt_ver"<<std::endl;
1165 m_server_ser_ver = deployed;
1167 // Get player position
1168 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1169 if(datasize >= 2+1+6)
1170 playerpos_s16 = readV3S16(&data[2+1]);
1171 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1174 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1176 // Set player position
1177 Player *player = m_env.getLocalPlayer();
1178 assert(player != NULL);
1179 player->setPosition(playerpos_f);
1182 if(datasize >= 2+1+6+8)
1185 m_map_seed = readU64(&data[2+1+6]);
1186 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1189 if(datasize >= 2+1+6+8+4)
1192 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1193 infostream<<"Client: received recommended send interval "
1194 <<m_recommended_send_interval<<std::endl;
1199 SharedBuffer<u8> reply(replysize);
1200 writeU16(&reply[0], TOSERVER_INIT2);
1202 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1207 if(command == TOCLIENT_ACCESS_DENIED)
1209 // The server didn't like our password. Note, this needs
1210 // to be processed even if the serialisation format has
1211 // not been agreed yet, the same as TOCLIENT_INIT.
1212 m_access_denied = true;
1213 m_access_denied_reason = L"Unknown";
1216 std::string datastring((char*)&data[2], datasize-2);
1217 std::istringstream is(datastring, std::ios_base::binary);
1218 m_access_denied_reason = deSerializeWideString(is);
1223 if(ser_version == SER_FMT_VER_INVALID)
1225 infostream<<"Client: Server serialization"
1226 " format invalid or not initialized."
1227 " Skipping incoming command="<<command<<std::endl;
1231 // Just here to avoid putting the two if's together when
1232 // making some copypasta
1235 if(command == TOCLIENT_REMOVENODE)
1240 p.X = readS16(&data[2]);
1241 p.Y = readS16(&data[4]);
1242 p.Z = readS16(&data[6]);
1244 //TimeTaker t1("TOCLIENT_REMOVENODE");
1248 else if(command == TOCLIENT_ADDNODE)
1250 if(datasize < 8 + MapNode::serializedLength(ser_version))
1254 p.X = readS16(&data[2]);
1255 p.Y = readS16(&data[4]);
1256 p.Z = readS16(&data[6]);
1258 //TimeTaker t1("TOCLIENT_ADDNODE");
1261 n.deSerialize(&data[8], ser_version);
1265 else if(command == TOCLIENT_BLOCKDATA)
1267 // Ignore too small packet
1272 p.X = readS16(&data[2]);
1273 p.Y = readS16(&data[4]);
1274 p.Z = readS16(&data[6]);
1276 /*infostream<<"Client: Thread: BLOCKDATA for ("
1277 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1278 /*infostream<<"Client: Thread: BLOCKDATA for ("
1279 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1281 std::string datastring((char*)&data[8], datasize-8);
1282 std::istringstream istr(datastring, std::ios_base::binary);
1287 v2s16 p2d(p.X, p.Z);
1288 sector = m_env.getMap().emergeSector(p2d);
1290 assert(sector->getPos() == p2d);
1292 //TimeTaker timer("MapBlock deSerialize");
1295 block = sector->getBlockNoCreateNoEx(p.Y);
1299 Update an existing block
1301 //infostream<<"Updating"<<std::endl;
1302 block->deSerialize(istr, ser_version, false);
1309 //infostream<<"Creating new"<<std::endl;
1310 block = new MapBlock(&m_env.getMap(), p, this);
1311 block->deSerialize(istr, ser_version, false);
1312 sector->insertBlock(block);
1326 u32 replysize = 2+1+6;
1327 SharedBuffer<u8> reply(replysize);
1328 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1330 writeV3S16(&reply[3], p);
1332 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1336 Add it to mesh update queue and set it to be acknowledged after update.
1338 //infostream<<"Adding mesh update task for received block"<<std::endl;
1339 addUpdateMeshTaskWithEdge(p, true);
1341 else if(command == TOCLIENT_INVENTORY)
1346 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1349 //TimeTaker t2("mutex locking", m_device);
1350 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1353 //TimeTaker t3("istringstream init", m_device);
1354 std::string datastring((char*)&data[2], datasize-2);
1355 std::istringstream is(datastring, std::ios_base::binary);
1358 //m_env.printPlayers(infostream);
1360 //TimeTaker t4("player get", m_device);
1361 Player *player = m_env.getLocalPlayer();
1362 assert(player != NULL);
1365 //TimeTaker t1("inventory.deSerialize()", m_device);
1366 player->inventory.deSerialize(is);
1369 m_inventory_updated = true;
1371 delete m_inventory_from_server;
1372 m_inventory_from_server = new Inventory(player->inventory);
1373 m_inventory_from_server_age = 0.0;
1375 //infostream<<"Client got player inventory:"<<std::endl;
1376 //player->inventory.print(infostream);
1379 else if(command == TOCLIENT_TIME_OF_DAY)
1384 u16 time_of_day = readU16(&data[2]);
1385 time_of_day = time_of_day % 24000;
1386 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1387 float time_speed = 0;
1388 if(datasize >= 2 + 2 + 4){
1389 time_speed = readF1000(&data[4]);
1391 // Old message; try to approximate speed of time by ourselves
1392 float time_of_day_f = (float)time_of_day / 24000.0;
1393 float tod_diff_f = 0;
1394 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1395 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1397 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1398 m_last_time_of_day_f = time_of_day_f;
1399 float time_diff = m_time_of_day_update_timer;
1400 m_time_of_day_update_timer = 0;
1401 if(m_time_of_day_set){
1402 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1403 infostream<<"Client: Measured time_of_day speed (old format): "
1404 <<time_speed<<" tod_diff_f="<<tod_diff_f
1405 <<" time_diff="<<time_diff<<std::endl;
1409 // Update environment
1410 m_env.setTimeOfDay(time_of_day);
1411 m_env.setTimeOfDaySpeed(time_speed);
1412 m_time_of_day_set = true;
1414 u32 dr = m_env.getDayNightRatio();
1415 verbosestream<<"Client: time_of_day="<<time_of_day
1416 <<" time_speed="<<time_speed
1417 <<" dr="<<dr<<std::endl;
1419 else if(command == TOCLIENT_CHAT_MESSAGE)
1427 std::string datastring((char*)&data[2], datasize-2);
1428 std::istringstream is(datastring, std::ios_base::binary);
1431 is.read((char*)buf, 2);
1432 u16 len = readU16(buf);
1434 std::wstring message;
1435 for(u16 i=0; i<len; i++)
1437 is.read((char*)buf, 2);
1438 message += (wchar_t)readU16(buf);
1441 /*infostream<<"Client received chat message: "
1442 <<wide_to_narrow(message)<<std::endl;*/
1444 m_chat_queue.push_back(message);
1446 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1448 //if(g_settings->getBool("enable_experimental"))
1452 u16 count of removed objects
1453 for all removed objects {
1456 u16 count of added objects
1457 for all added objects {
1460 u32 initialization data length
1461 string initialization data
1466 // Get all data except the command number
1467 std::string datastring((char*)&data[2], datasize-2);
1468 // Throw them in an istringstream
1469 std::istringstream is(datastring, std::ios_base::binary);
1473 // Read removed objects
1475 u16 removed_count = readU16((u8*)buf);
1476 for(u16 i=0; i<removed_count; i++)
1479 u16 id = readU16((u8*)buf);
1482 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1483 m_env.removeActiveObject(id);
1487 // Read added objects
1489 u16 added_count = readU16((u8*)buf);
1490 for(u16 i=0; i<added_count; i++)
1493 u16 id = readU16((u8*)buf);
1495 u8 type = readU8((u8*)buf);
1496 std::string data = deSerializeLongString(is);
1499 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1500 m_env.addActiveObject(id, type, data);
1505 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1507 //if(g_settings->getBool("enable_experimental"))
1519 // Get all data except the command number
1520 std::string datastring((char*)&data[2], datasize-2);
1521 // Throw them in an istringstream
1522 std::istringstream is(datastring, std::ios_base::binary);
1524 while(is.eof() == false)
1528 u16 id = readU16((u8*)buf);
1532 u16 message_size = readU16((u8*)buf);
1533 std::string message;
1534 message.reserve(message_size);
1535 for(u16 i=0; i<message_size; i++)
1538 message.append(buf, 1);
1540 // Pass on to the environment
1542 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1543 m_env.processActiveObjectMessage(id, message);
1548 else if(command == TOCLIENT_MOVEMENT)
1550 std::string datastring((char*)&data[2], datasize-2);
1551 std::istringstream is(datastring, std::ios_base::binary);
1552 Player *player = m_env.getLocalPlayer();
1553 assert(player != NULL);
1555 player->movement_acceleration_default = readF1000(is) * BS;
1556 player->movement_acceleration_air = readF1000(is) * BS;
1557 player->movement_acceleration_fast = readF1000(is) * BS;
1558 player->movement_speed_walk = readF1000(is) * BS;
1559 player->movement_speed_crouch = readF1000(is) * BS;
1560 player->movement_speed_fast = readF1000(is) * BS;
1561 player->movement_speed_climb = readF1000(is) * BS;
1562 player->movement_speed_jump = readF1000(is) * BS;
1563 player->movement_liquid_fluidity = readF1000(is) * BS;
1564 player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
1565 player->movement_liquid_sink = readF1000(is) * BS;
1566 player->movement_gravity = readF1000(is) * BS;
1568 else if(command == TOCLIENT_HP)
1570 std::string datastring((char*)&data[2], datasize-2);
1571 std::istringstream is(datastring, std::ios_base::binary);
1572 Player *player = m_env.getLocalPlayer();
1573 assert(player != NULL);
1574 u8 oldhp = player->hp;
1580 // Add to ClientEvent queue
1582 event.type = CE_PLAYER_DAMAGE;
1583 event.player_damage.amount = oldhp - hp;
1584 m_client_event_queue.push_back(event);
1587 else if(command == TOCLIENT_MOVE_PLAYER)
1589 std::string datastring((char*)&data[2], datasize-2);
1590 std::istringstream is(datastring, std::ios_base::binary);
1591 Player *player = m_env.getLocalPlayer();
1592 assert(player != NULL);
1593 v3f pos = readV3F1000(is);
1594 f32 pitch = readF1000(is);
1595 f32 yaw = readF1000(is);
1596 player->setPosition(pos);
1597 /*player->setPitch(pitch);
1598 player->setYaw(yaw);*/
1600 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1601 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1607 Add to ClientEvent queue.
1608 This has to be sent to the main program because otherwise
1609 it would just force the pitch and yaw values to whatever
1610 the camera points to.
1613 event.type = CE_PLAYER_FORCE_MOVE;
1614 event.player_force_move.pitch = pitch;
1615 event.player_force_move.yaw = yaw;
1616 m_client_event_queue.push_back(event);
1618 // Ignore damage for a few seconds, so that the player doesn't
1619 // get damage from falling on ground
1620 m_ignore_damage_timer = 3.0;
1622 else if(command == TOCLIENT_PLAYERITEM)
1624 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1626 else if(command == TOCLIENT_DEATHSCREEN)
1628 std::string datastring((char*)&data[2], datasize-2);
1629 std::istringstream is(datastring, std::ios_base::binary);
1631 bool set_camera_point_target = readU8(is);
1632 v3f camera_point_target = readV3F1000(is);
1635 event.type = CE_DEATHSCREEN;
1636 event.deathscreen.set_camera_point_target = set_camera_point_target;
1637 event.deathscreen.camera_point_target_x = camera_point_target.X;
1638 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1639 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1640 m_client_event_queue.push_back(event);
1642 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1644 std::string datastring((char*)&data[2], datasize-2);
1645 std::istringstream is(datastring, std::ios_base::binary);
1647 // Mesh update thread must be stopped while
1648 // updating content definitions
1649 assert(!m_mesh_update_thread.IsRunning());
1651 int num_files = readU16(is);
1653 infostream<<"Client: Received media announcement: packet size: "
1654 <<datasize<<std::endl;
1656 std::list<MediaRequest> file_requests;
1658 for(int i=0; i<num_files; i++)
1660 //read file from cache
1661 std::string name = deSerializeString(is);
1662 std::string sha1_base64 = deSerializeString(is);
1664 // if name contains illegal characters, ignore the file
1665 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1666 errorstream<<"Client: ignoring illegal file name "
1667 <<"sent by server: \""<<name<<"\""<<std::endl;
1671 std::string sha1_raw = base64_decode(sha1_base64);
1672 std::string sha1_hex = hex_encode(sha1_raw);
1673 std::ostringstream tmp_os(std::ios_base::binary);
1674 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1675 m_media_name_sha1_map[name] = sha1_raw;
1677 // If found in cache, try to load it from there
1680 bool success = loadMedia(tmp_os.str(), name);
1682 verbosestream<<"Client: Loaded cached media: "
1683 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1686 infostream<<"Client: Failed to load cached media: "
1687 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1690 // Didn't load from cache; queue it to be requested
1691 verbosestream<<"Client: Adding file to request list: \""
1692 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1693 file_requests.push_back(MediaRequest(name));
1696 std::string remote_media = "";
1698 remote_media = deSerializeString(is);
1700 catch(SerializationError) {
1701 // not supported by server or turned off
1704 m_media_count = file_requests.size();
1705 m_media_receive_started = true;
1707 if (remote_media == "" || !USE_CURL) {
1708 request_media(file_requests);
1711 std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin();
1712 for(std::list<MediaRequest>::iterator i = file_requests.begin();
1713 i != file_requests.end(); ++i) {
1714 (*cur)->m_file_requests.push_back(*i);
1716 if (cur == m_media_fetch_threads.end())
1717 cur = m_media_fetch_threads.begin();
1719 for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin();
1720 i != m_media_fetch_threads.end(); ++i) {
1721 (*i)->m_remote_url = remote_media;
1726 // notify server we received everything
1727 std::ostringstream os(std::ios_base::binary);
1728 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1729 std::string s = os.str();
1730 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1732 Send(0, data, true);
1735 event.type = CE_TEXTURES_UPDATED;
1736 m_client_event_queue.push_back(event);
1738 else if(command == TOCLIENT_MEDIA)
1740 if (m_media_count == 0)
1742 std::string datastring((char*)&data[2], datasize-2);
1743 std::istringstream is(datastring, std::ios_base::binary);
1745 // Mesh update thread must be stopped while
1746 // updating content definitions
1747 assert(!m_mesh_update_thread.IsRunning());
1751 u16 total number of file bunches
1752 u16 index of this bunch
1753 u32 number of files in this bunch
1761 int num_bunches = readU16(is);
1762 int bunch_i = readU16(is);
1763 int num_files = readU32(is);
1764 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1765 <<num_bunches<<" files="<<num_files
1766 <<" size="<<datasize<<std::endl;
1767 for(int i=0; i<num_files; i++){
1768 m_media_received_count++;
1769 std::string name = deSerializeString(is);
1770 std::string data = deSerializeLongString(is);
1772 // if name contains illegal characters, ignore the file
1773 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1774 errorstream<<"Client: ignoring illegal file name "
1775 <<"sent by server: \""<<name<<"\""<<std::endl;
1779 bool success = loadMedia(data, name);
1781 verbosestream<<"Client: Loaded received media: "
1782 <<"\""<<name<<"\". Caching."<<std::endl;
1784 infostream<<"Client: Failed to load received media: "
1785 <<"\""<<name<<"\". Not caching."<<std::endl;
1789 bool did = fs::CreateAllDirs(getMediaCacheDir());
1791 errorstream<<"Could not create media cache directory"
1796 std::map<std::string, std::string>::iterator n;
1797 n = m_media_name_sha1_map.find(name);
1798 if(n == m_media_name_sha1_map.end())
1799 errorstream<<"The server sent a file that has not "
1800 <<"been announced."<<std::endl;
1802 m_media_cache.update_sha1(data);
1807 event.type = CE_TEXTURES_UPDATED;
1808 m_client_event_queue.push_back(event);
1810 else if(command == TOCLIENT_TOOLDEF)
1812 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1814 else if(command == TOCLIENT_NODEDEF)
1816 infostream<<"Client: Received node definitions: packet size: "
1817 <<datasize<<std::endl;
1819 // Mesh update thread must be stopped while
1820 // updating content definitions
1821 assert(!m_mesh_update_thread.IsRunning());
1823 // Decompress node definitions
1824 std::string datastring((char*)&data[2], datasize-2);
1825 std::istringstream is(datastring, std::ios_base::binary);
1826 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1827 std::ostringstream tmp_os;
1828 decompressZlib(tmp_is, tmp_os);
1830 // Deserialize node definitions
1831 std::istringstream tmp_is2(tmp_os.str());
1832 m_nodedef->deSerialize(tmp_is2);
1833 m_nodedef_received = true;
1835 else if(command == TOCLIENT_CRAFTITEMDEF)
1837 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1839 else if(command == TOCLIENT_ITEMDEF)
1841 infostream<<"Client: Received item definitions: packet size: "
1842 <<datasize<<std::endl;
1844 // Mesh update thread must be stopped while
1845 // updating content definitions
1846 assert(!m_mesh_update_thread.IsRunning());
1848 // Decompress item definitions
1849 std::string datastring((char*)&data[2], datasize-2);
1850 std::istringstream is(datastring, std::ios_base::binary);
1851 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1852 std::ostringstream tmp_os;
1853 decompressZlib(tmp_is, tmp_os);
1855 // Deserialize node definitions
1856 std::istringstream tmp_is2(tmp_os.str());
1857 m_itemdef->deSerialize(tmp_is2);
1858 m_itemdef_received = true;
1860 else if(command == TOCLIENT_PLAY_SOUND)
1862 std::string datastring((char*)&data[2], datasize-2);
1863 std::istringstream is(datastring, std::ios_base::binary);
1865 s32 server_id = readS32(is);
1866 std::string name = deSerializeString(is);
1867 float gain = readF1000(is);
1868 int type = readU8(is); // 0=local, 1=positional, 2=object
1869 v3f pos = readV3F1000(is);
1870 u16 object_id = readU16(is);
1871 bool loop = readU8(is);
1876 client_id = m_sound->playSound(name, loop, gain);
1878 case 1: // positional
1879 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1882 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1884 pos = cao->getPosition();
1885 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1886 // TODO: Set up sound to move with object
1891 if(client_id != -1){
1892 m_sounds_server_to_client[server_id] = client_id;
1893 m_sounds_client_to_server[client_id] = server_id;
1895 m_sounds_to_objects[client_id] = object_id;
1898 else if(command == TOCLIENT_STOP_SOUND)
1900 std::string datastring((char*)&data[2], datasize-2);
1901 std::istringstream is(datastring, std::ios_base::binary);
1903 s32 server_id = readS32(is);
1904 std::map<s32, int>::iterator i =
1905 m_sounds_server_to_client.find(server_id);
1906 if(i != m_sounds_server_to_client.end()){
1907 int client_id = i->second;
1908 m_sound->stopSound(client_id);
1911 else if(command == TOCLIENT_PRIVILEGES)
1913 std::string datastring((char*)&data[2], datasize-2);
1914 std::istringstream is(datastring, std::ios_base::binary);
1916 m_privileges.clear();
1917 infostream<<"Client: Privileges updated: ";
1918 u16 num_privileges = readU16(is);
1919 for(u16 i=0; i<num_privileges; i++){
1920 std::string priv = deSerializeString(is);
1921 m_privileges.insert(priv);
1922 infostream<<priv<<" ";
1924 infostream<<std::endl;
1926 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1928 std::string datastring((char*)&data[2], datasize-2);
1929 std::istringstream is(datastring, std::ios_base::binary);
1931 // Store formspec in LocalPlayer
1932 Player *player = m_env.getLocalPlayer();
1933 assert(player != NULL);
1934 player->inventory_formspec = deSerializeLongString(is);
1936 else if(command == TOCLIENT_DETACHED_INVENTORY)
1938 std::string datastring((char*)&data[2], datasize-2);
1939 std::istringstream is(datastring, std::ios_base::binary);
1941 std::string name = deSerializeString(is);
1943 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1945 Inventory *inv = NULL;
1946 if(m_detached_inventories.count(name) > 0)
1947 inv = m_detached_inventories[name];
1949 inv = new Inventory(m_itemdef);
1950 m_detached_inventories[name] = inv;
1952 inv->deSerialize(is);
1954 else if(command == TOCLIENT_SHOW_FORMSPEC)
1956 std::string datastring((char*)&data[2], datasize-2);
1957 std::istringstream is(datastring, std::ios_base::binary);
1959 std::string formspec = deSerializeLongString(is);
1960 std::string formname = deSerializeString(is);
1963 event.type = CE_SHOW_FORMSPEC;
1964 // pointer is required as event is a struct only!
1965 // adding a std:string to a struct isn't possible
1966 event.show_formspec.formspec = new std::string(formspec);
1967 event.show_formspec.formname = new std::string(formname);
1968 m_client_event_queue.push_back(event);
1970 else if(command == TOCLIENT_SPAWN_PARTICLE)
1972 std::string datastring((char*)&data[2], datasize-2);
1973 std::istringstream is(datastring, std::ios_base::binary);
1975 v3f pos = readV3F1000(is);
1976 v3f vel = readV3F1000(is);
1977 v3f acc = readV3F1000(is);
1978 float expirationtime = readF1000(is);
1979 float size = readF1000(is);
1980 bool collisiondetection = readU8(is);
1981 std::string texture = deSerializeLongString(is);
1984 event.type = CE_SPAWN_PARTICLE;
1985 event.spawn_particle.pos = new v3f (pos);
1986 event.spawn_particle.vel = new v3f (vel);
1987 event.spawn_particle.acc = new v3f (acc);
1989 event.spawn_particle.expirationtime = expirationtime;
1990 event.spawn_particle.size = size;
1991 event.spawn_particle.collisiondetection =
1993 event.spawn_particle.texture = new std::string(texture);
1995 m_client_event_queue.push_back(event);
1997 else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
1999 std::string datastring((char*)&data[2], datasize-2);
2000 std::istringstream is(datastring, std::ios_base::binary);
2002 u16 amount = readU16(is);
2003 float spawntime = readF1000(is);
2004 v3f minpos = readV3F1000(is);
2005 v3f maxpos = readV3F1000(is);
2006 v3f minvel = readV3F1000(is);
2007 v3f maxvel = readV3F1000(is);
2008 v3f minacc = readV3F1000(is);
2009 v3f maxacc = readV3F1000(is);
2010 float minexptime = readF1000(is);
2011 float maxexptime = readF1000(is);
2012 float minsize = readF1000(is);
2013 float maxsize = readF1000(is);
2014 bool collisiondetection = readU8(is);
2015 std::string texture = deSerializeLongString(is);
2016 u32 id = readU32(is);
2019 event.type = CE_ADD_PARTICLESPAWNER;
2020 event.add_particlespawner.amount = amount;
2021 event.add_particlespawner.spawntime = spawntime;
2023 event.add_particlespawner.minpos = new v3f (minpos);
2024 event.add_particlespawner.maxpos = new v3f (maxpos);
2025 event.add_particlespawner.minvel = new v3f (minvel);
2026 event.add_particlespawner.maxvel = new v3f (maxvel);
2027 event.add_particlespawner.minacc = new v3f (minacc);
2028 event.add_particlespawner.maxacc = new v3f (maxacc);
2030 event.add_particlespawner.minexptime = minexptime;
2031 event.add_particlespawner.maxexptime = maxexptime;
2032 event.add_particlespawner.minsize = minsize;
2033 event.add_particlespawner.maxsize = maxsize;
2034 event.add_particlespawner.collisiondetection = collisiondetection;
2035 event.add_particlespawner.texture = new std::string(texture);
2036 event.add_particlespawner.id = id;
2038 m_client_event_queue.push_back(event);
2040 else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
2042 std::string datastring((char*)&data[2], datasize-2);
2043 std::istringstream is(datastring, std::ios_base::binary);
2045 u32 id = readU16(is);
2048 event.type = CE_DELETE_PARTICLESPAWNER;
2049 event.delete_particlespawner.id = id;
2051 m_client_event_queue.push_back(event);
2053 else if(command == TOCLIENT_HUDADD)
2055 std::string datastring((char *)&data[2], datasize - 2);
2056 std::istringstream is(datastring, std::ios_base::binary);
2058 u32 id = readU32(is);
2059 u8 type = readU8(is);
2060 v2f pos = readV2F1000(is);
2061 std::string name = deSerializeString(is);
2062 v2f scale = readV2F1000(is);
2063 std::string text = deSerializeString(is);
2064 u32 number = readU32(is);
2065 u32 item = readU32(is);
2066 u32 dir = readU32(is);
2067 v2f align = readV2F1000(is);
2068 v2f offset = readV2F1000(is);
2071 event.type = CE_HUDADD;
2072 event.hudadd.id = id;
2073 event.hudadd.type = type;
2074 event.hudadd.pos = new v2f(pos);
2075 event.hudadd.name = new std::string(name);
2076 event.hudadd.scale = new v2f(scale);
2077 event.hudadd.text = new std::string(text);
2078 event.hudadd.number = number;
2079 event.hudadd.item = item;
2080 event.hudadd.dir = dir;
2081 event.hudadd.align = new v2f(align);
2082 event.hudadd.offset = new v2f(offset);
2083 m_client_event_queue.push_back(event);
2085 else if(command == TOCLIENT_HUDRM)
2087 std::string datastring((char *)&data[2], datasize - 2);
2088 std::istringstream is(datastring, std::ios_base::binary);
2090 u32 id = readU32(is);
2093 event.type = CE_HUDRM;
2094 event.hudrm.id = id;
2095 m_client_event_queue.push_back(event);
2097 else if(command == TOCLIENT_HUDCHANGE)
2103 std::string datastring((char *)&data[2], datasize - 2);
2104 std::istringstream is(datastring, std::ios_base::binary);
2106 u32 id = readU32(is);
2107 u8 stat = (HudElementStat)readU8(is);
2109 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
2110 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
2111 v2fdata = readV2F1000(is);
2112 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
2113 sdata = deSerializeString(is);
2115 intdata = readU32(is);
2118 event.type = CE_HUDCHANGE;
2119 event.hudchange.id = id;
2120 event.hudchange.stat = (HudElementStat)stat;
2121 event.hudchange.v2fdata = new v2f(v2fdata);
2122 event.hudchange.sdata = new std::string(sdata);
2123 event.hudchange.data = intdata;
2124 m_client_event_queue.push_back(event);
2126 else if(command == TOCLIENT_HUD_SET_FLAGS)
2128 std::string datastring((char *)&data[2], datasize - 2);
2129 std::istringstream is(datastring, std::ios_base::binary);
2131 Player *player = m_env.getLocalPlayer();
2132 assert(player != NULL);
2134 u32 flags = readU32(is);
2135 u32 mask = readU32(is);
2137 player->hud_flags &= ~mask;
2138 player->hud_flags |= flags;
2140 else if(command == TOCLIENT_HUD_SET_PARAM)
2142 std::string datastring((char *)&data[2], datasize - 2);
2143 std::istringstream is(datastring, std::ios_base::binary);
2145 Player *player = m_env.getLocalPlayer();
2146 assert(player != NULL);
2148 u16 param = readU16(is);
2149 std::string value = deSerializeString(is);
2151 if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){
2152 s32 hotbar_itemcount = readS32((u8*) value.c_str());
2153 if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
2154 player->hud_hotbar_itemcount = hotbar_itemcount;
2159 infostream<<"Client: Ignoring unknown command "
2160 <<command<<std::endl;
2164 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
2166 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2167 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
2170 void Client::interact(u8 action, const PointedThing& pointed)
2172 if(connectedAndInitialized() == false){
2173 infostream<<"Client::interact() "
2174 "cancelled (not connected)"
2179 std::ostringstream os(std::ios_base::binary);
2185 [5] u32 length of the next item
2186 [9] serialized PointedThing
2188 0: start digging (from undersurface) or use
2189 1: stop digging (all parameters ignored)
2190 2: digging completed
2191 3: place block or item (to abovesurface)
2194 writeU16(os, TOSERVER_INTERACT);
2195 writeU8(os, action);
2196 writeU16(os, getPlayerItem());
2197 std::ostringstream tmp_os(std::ios::binary);
2198 pointed.serialize(tmp_os);
2199 os<<serializeLongString(tmp_os.str());
2201 std::string s = os.str();
2202 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2205 Send(0, data, true);
2208 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
2209 const std::map<std::string, std::string> &fields)
2211 std::ostringstream os(std::ios_base::binary);
2213 writeU16(os, TOSERVER_NODEMETA_FIELDS);
2215 os<<serializeString(formname);
2216 writeU16(os, fields.size());
2217 for(std::map<std::string, std::string>::const_iterator
2218 i = fields.begin(); i != fields.end(); i++){
2219 const std::string &name = i->first;
2220 const std::string &value = i->second;
2221 os<<serializeString(name);
2222 os<<serializeLongString(value);
2226 std::string s = os.str();
2227 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2229 Send(0, data, true);
2232 void Client::sendInventoryFields(const std::string &formname,
2233 const std::map<std::string, std::string> &fields)
2235 std::ostringstream os(std::ios_base::binary);
2237 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2238 os<<serializeString(formname);
2239 writeU16(os, fields.size());
2240 for(std::map<std::string, std::string>::const_iterator
2241 i = fields.begin(); i != fields.end(); i++){
2242 const std::string &name = i->first;
2243 const std::string &value = i->second;
2244 os<<serializeString(name);
2245 os<<serializeLongString(value);
2249 std::string s = os.str();
2250 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2252 Send(0, data, true);
2255 void Client::sendInventoryAction(InventoryAction *a)
2257 std::ostringstream os(std::ios_base::binary);
2261 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2262 os.write((char*)buf, 2);
2267 std::string s = os.str();
2268 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2270 Send(0, data, true);
2273 void Client::sendChatMessage(const std::wstring &message)
2275 std::ostringstream os(std::ios_base::binary);
2279 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2280 os.write((char*)buf, 2);
2283 writeU16(buf, message.size());
2284 os.write((char*)buf, 2);
2287 for(u32 i=0; i<message.size(); i++)
2291 os.write((char*)buf, 2);
2295 std::string s = os.str();
2296 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2298 Send(0, data, true);
2301 void Client::sendChangePassword(const std::wstring oldpassword,
2302 const std::wstring newpassword)
2304 Player *player = m_env.getLocalPlayer();
2308 std::string playername = player->getName();
2309 std::string oldpwd = translatePassword(playername, oldpassword);
2310 std::string newpwd = translatePassword(playername, newpassword);
2312 std::ostringstream os(std::ios_base::binary);
2313 u8 buf[2+PASSWORD_SIZE*2];
2315 [0] u16 TOSERVER_PASSWORD
2316 [2] u8[28] old password
2317 [30] u8[28] new password
2320 writeU16(buf, TOSERVER_PASSWORD);
2321 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2323 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2324 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2326 buf[2+PASSWORD_SIZE-1] = 0;
2327 buf[30+PASSWORD_SIZE-1] = 0;
2328 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2331 std::string s = os.str();
2332 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2334 Send(0, data, true);
2338 void Client::sendDamage(u8 damage)
2340 DSTACK(__FUNCTION_NAME);
2341 std::ostringstream os(std::ios_base::binary);
2343 writeU16(os, TOSERVER_DAMAGE);
2344 writeU8(os, damage);
2347 std::string s = os.str();
2348 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2350 Send(0, data, true);
2353 void Client::sendRespawn()
2355 DSTACK(__FUNCTION_NAME);
2356 std::ostringstream os(std::ios_base::binary);
2358 writeU16(os, TOSERVER_RESPAWN);
2361 std::string s = os.str();
2362 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2364 Send(0, data, true);
2367 void Client::sendPlayerPos()
2369 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2371 LocalPlayer *myplayer = m_env.getLocalPlayer();
2372 if(myplayer == NULL)
2375 // Save bandwidth by only updating position when something changed
2376 if(myplayer->last_position == myplayer->getPosition() &&
2377 myplayer->last_speed == myplayer->getSpeed() &&
2378 myplayer->last_pitch == myplayer->getPitch() &&
2379 myplayer->last_yaw == myplayer->getYaw() &&
2380 myplayer->last_keyPressed == myplayer->keyPressed)
2383 myplayer->last_position = myplayer->getPosition();
2384 myplayer->last_speed = myplayer->getSpeed();
2385 myplayer->last_pitch = myplayer->getPitch();
2386 myplayer->last_yaw = myplayer->getYaw();
2387 myplayer->last_keyPressed = myplayer->keyPressed;
2391 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2392 our_peer_id = m_con.GetPeerID();
2395 // Set peer id if not set already
2396 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2397 myplayer->peer_id = our_peer_id;
2398 // Check that an existing peer_id is the same as the connection's
2399 assert(myplayer->peer_id == our_peer_id);
2401 v3f pf = myplayer->getPosition();
2402 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2403 v3f sf = myplayer->getSpeed();
2404 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2405 s32 pitch = myplayer->getPitch() * 100;
2406 s32 yaw = myplayer->getYaw() * 100;
2407 u32 keyPressed=myplayer->keyPressed;
2411 [2] v3s32 position*100
2412 [2+12] v3s32 speed*100
2413 [2+12+12] s32 pitch*100
2414 [2+12+12+4] s32 yaw*100
2415 [2+12+12+4+4] u32 keyPressed
2417 SharedBuffer<u8> data(2+12+12+4+4+4);
2418 writeU16(&data[0], TOSERVER_PLAYERPOS);
2419 writeV3S32(&data[2], position);
2420 writeV3S32(&data[2+12], speed);
2421 writeS32(&data[2+12+12], pitch);
2422 writeS32(&data[2+12+12+4], yaw);
2423 writeU32(&data[2+12+12+4+4], keyPressed);
2424 // Send as unreliable
2425 Send(0, data, false);
2428 void Client::sendPlayerItem(u16 item)
2430 Player *myplayer = m_env.getLocalPlayer();
2431 if(myplayer == NULL)
2434 u16 our_peer_id = m_con.GetPeerID();
2436 // Set peer id if not set already
2437 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2438 myplayer->peer_id = our_peer_id;
2439 // Check that an existing peer_id is the same as the connection's
2440 assert(myplayer->peer_id == our_peer_id);
2442 SharedBuffer<u8> data(2+2);
2443 writeU16(&data[0], TOSERVER_PLAYERITEM);
2444 writeU16(&data[2], item);
2447 Send(0, data, true);
2450 void Client::removeNode(v3s16 p)
2452 std::map<v3s16, MapBlock*> modified_blocks;
2456 //TimeTaker t("removeNodeAndUpdate", m_device);
2457 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2459 catch(InvalidPositionException &e)
2463 // add urgent task to update the modified node
2464 addUpdateMeshTaskForNode(p, false, true);
2466 for(std::map<v3s16, MapBlock * >::iterator
2467 i = modified_blocks.begin();
2468 i != modified_blocks.end(); ++i)
2470 addUpdateMeshTaskWithEdge(i->first);
2474 void Client::addNode(v3s16 p, MapNode n)
2476 TimeTaker timer1("Client::addNode()");
2478 std::map<v3s16, MapBlock*> modified_blocks;
2482 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2483 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2485 catch(InvalidPositionException &e)
2488 for(std::map<v3s16, MapBlock * >::iterator
2489 i = modified_blocks.begin();
2490 i != modified_blocks.end(); ++i)
2492 addUpdateMeshTaskWithEdge(i->first);
2496 void Client::setPlayerControl(PlayerControl &control)
2498 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2499 LocalPlayer *player = m_env.getLocalPlayer();
2500 assert(player != NULL);
2501 player->control = control;
2504 void Client::selectPlayerItem(u16 item)
2506 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2507 m_playeritem = item;
2508 m_inventory_updated = true;
2509 sendPlayerItem(item);
2512 // Returns true if the inventory of the local player has been
2513 // updated from the server. If it is true, it is set to false.
2514 bool Client::getLocalInventoryUpdated()
2516 // m_inventory_updated is behind envlock
2517 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2518 bool updated = m_inventory_updated;
2519 m_inventory_updated = false;
2523 // Copies the inventory of the local player to parameter
2524 void Client::getLocalInventory(Inventory &dst)
2526 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2527 Player *player = m_env.getLocalPlayer();
2528 assert(player != NULL);
2529 dst = player->inventory;
2532 Inventory* Client::getInventory(const InventoryLocation &loc)
2535 case InventoryLocation::UNDEFINED:
2538 case InventoryLocation::CURRENT_PLAYER:
2540 Player *player = m_env.getLocalPlayer();
2541 assert(player != NULL);
2542 return &player->inventory;
2545 case InventoryLocation::PLAYER:
2547 Player *player = m_env.getPlayer(loc.name.c_str());
2550 return &player->inventory;
2553 case InventoryLocation::NODEMETA:
2555 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2558 return meta->getInventory();
2561 case InventoryLocation::DETACHED:
2563 if(m_detached_inventories.count(loc.name) == 0)
2565 return m_detached_inventories[loc.name];
2573 void Client::inventoryAction(InventoryAction *a)
2576 Send it to the server
2578 sendInventoryAction(a);
2581 Predict some local inventory changes
2583 a->clientApply(this, this);
2589 ClientActiveObject * Client::getSelectedActiveObject(
2591 v3f from_pos_f_on_map,
2592 core::line3d<f32> shootline_on_map
2595 std::vector<DistanceSortedActiveObject> objects;
2597 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2599 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2602 // After this, the closest object is the first in the array.
2603 std::sort(objects.begin(), objects.end());
2605 for(u32 i=0; i<objects.size(); i++)
2607 ClientActiveObject *obj = objects[i].obj;
2609 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2610 if(selection_box == NULL)
2613 v3f pos = obj->getPosition();
2615 core::aabbox3d<f32> offsetted_box(
2616 selection_box->MinEdge + pos,
2617 selection_box->MaxEdge + pos
2620 if(offsetted_box.intersectsWithLine(shootline_on_map))
2622 //infostream<<"Returning selected object"<<std::endl;
2627 //infostream<<"No object selected; returning NULL."<<std::endl;
2631 void Client::printDebugInfo(std::ostream &os)
2633 //JMutexAutoLock lock1(m_fetchblock_mutex);
2634 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2636 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2637 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2638 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2642 std::list<std::string> Client::getConnectedPlayerNames()
2644 return m_env.getPlayerNames();
2647 float Client::getAnimationTime()
2649 return m_animation_time;
2652 int Client::getCrackLevel()
2654 return m_crack_level;
2657 void Client::setCrack(int level, v3s16 pos)
2659 int old_crack_level = m_crack_level;
2660 v3s16 old_crack_pos = m_crack_pos;
2662 m_crack_level = level;
2665 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2668 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2670 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2673 addUpdateMeshTaskForNode(pos, false, true);
2679 Player *player = m_env.getLocalPlayer();
2680 assert(player != NULL);
2684 u16 Client::getBreath()
2686 Player *player = m_env.getLocalPlayer();
2687 assert(player != NULL);
2688 return player->breath;
2691 bool Client::getChatMessage(std::wstring &message)
2693 if(m_chat_queue.size() == 0)
2695 message = m_chat_queue.pop_front();
2699 void Client::typeChatMessage(const std::wstring &message)
2701 // Discard empty line
2706 sendChatMessage(message);
2709 if (message[0] == L'/')
2711 m_chat_queue.push_back(
2712 (std::wstring)L"issued command: "+message);
2716 LocalPlayer *player = m_env.getLocalPlayer();
2717 assert(player != NULL);
2718 std::wstring name = narrow_to_wide(player->getName());
2719 m_chat_queue.push_back(
2720 (std::wstring)L"<"+name+L"> "+message);
2724 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2726 /*infostream<<"Client::addUpdateMeshTask(): "
2727 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2728 <<" ack_to_server="<<ack_to_server
2729 <<" urgent="<<urgent
2732 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2737 Create a task to update the mesh of the block
2740 MeshMakeData *data = new MeshMakeData(this);
2743 //TimeTaker timer("data fill");
2745 // Debug: 1-6ms, avg=2ms
2747 data->setCrack(m_crack_level, m_crack_pos);
2748 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2752 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2754 // Add task to queue
2755 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2757 /*infostream<<"Mesh update input queue size is "
2758 <<m_mesh_update_thread.m_queue_in.size()
2762 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2766 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2767 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2772 v3s16 p = blockpos + v3s16(0,0,0);
2773 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2774 addUpdateMeshTask(p, ack_to_server, urgent);
2776 catch(InvalidPositionException &e){}
2778 for (int i=0;i<6;i++)
2781 v3s16 p = blockpos + g_6dirs[i];
2782 addUpdateMeshTask(p, false, urgent);
2784 catch(InvalidPositionException &e){}
2788 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2792 infostream<<"Client::addUpdateMeshTaskForNode(): "
2793 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2797 v3s16 blockpos = getNodeBlockPos(nodepos);
2798 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2801 v3s16 p = blockpos + v3s16(0,0,0);
2802 addUpdateMeshTask(p, ack_to_server, urgent);
2804 catch(InvalidPositionException &e){}
2806 if(nodepos.X == blockpos_relative.X){
2808 v3s16 p = blockpos + v3s16(-1,0,0);
2809 addUpdateMeshTask(p, false, urgent);
2811 catch(InvalidPositionException &e){}
2813 if(nodepos.Y == blockpos_relative.Y){
2815 v3s16 p = blockpos + v3s16(0,-1,0);
2816 addUpdateMeshTask(p, false, urgent);
2818 catch(InvalidPositionException &e){}
2820 if(nodepos.Z == blockpos_relative.Z){
2822 v3s16 p = blockpos + v3s16(0,0,-1);
2823 addUpdateMeshTask(p, false, urgent);
2825 catch(InvalidPositionException &e){}
2829 ClientEvent Client::getClientEvent()
2831 if(m_client_event_queue.size() == 0)
2834 event.type = CE_NONE;
2837 return m_client_event_queue.pop_front();
2840 void draw_load_screen(const std::wstring &text,
2841 IrrlichtDevice* device, gui::IGUIFont* font,
2842 float dtime=0 ,int percent=0, bool clouds=true);
2843 void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
2845 infostream<<"Client::afterContentReceived() started"<<std::endl;
2846 assert(m_itemdef_received);
2847 assert(m_nodedef_received);
2848 assert(texturesReceived());
2850 // remove the information about which checksum each texture
2852 m_media_name_sha1_map.clear();
2854 // Rebuild inherited images and recreate textures
2855 infostream<<"- Rebuilding images and textures"<<std::endl;
2856 m_tsrc->rebuildImagesAndTextures();
2858 // Update texture atlas
2859 infostream<<"- Updating texture atlas"<<std::endl;
2860 if(g_settings->getBool("enable_texture_atlas"))
2861 m_tsrc->buildMainAtlas(this);
2864 m_shsrc->rebuildShaders();
2866 // Update node aliases
2867 infostream<<"- Updating node aliases"<<std::endl;
2868 m_nodedef->updateAliases(m_itemdef);
2870 // Update node textures
2871 infostream<<"- Updating node textures"<<std::endl;
2872 m_nodedef->updateTextures(m_tsrc);
2874 // Preload item textures and meshes if configured to
2875 if(g_settings->getBool("preload_item_visuals"))
2877 verbosestream<<"Updating item textures and meshes"<<std::endl;
2878 wchar_t* text = wgettext("Item textures...");
2879 draw_load_screen(text,device,font,0,0);
2880 std::set<std::string> names = m_itemdef->getAll();
2881 size_t size = names.size();
2884 for(std::set<std::string>::const_iterator
2885 i = names.begin(); i != names.end(); ++i){
2886 // Asking for these caches the result
2887 m_itemdef->getInventoryTexture(*i, this);
2888 m_itemdef->getWieldMesh(*i, this);
2890 percent = count*100/size;
2891 if (count%50 == 0) // only update every 50 item
2892 draw_load_screen(text,device,font,0,percent);
2897 // Start mesh update thread after setting up content definitions
2898 infostream<<"- Starting mesh update thread"<<std::endl;
2899 m_mesh_update_thread.Start();
2901 infostream<<"Client::afterContentReceived() done"<<std::endl;
2904 float Client::getRTT(void)
2907 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2908 } catch(con::PeerNotFoundException &e){
2913 // IGameDef interface
2915 IItemDefManager* Client::getItemDefManager()
2919 INodeDefManager* Client::getNodeDefManager()
2923 ICraftDefManager* Client::getCraftDefManager()
2926 //return m_craftdef;
2928 ITextureSource* Client::getTextureSource()
2932 IShaderSource* Client::getShaderSource()
2936 u16 Client::allocateUnknownNodeId(const std::string &name)
2938 errorstream<<"Client::allocateUnknownNodeId(): "
2939 <<"Client cannot allocate node IDs"<<std::endl;
2941 return CONTENT_IGNORE;
2943 ISoundManager* Client::getSoundManager()
2947 MtEventManager* Client::getEventManager()