3 Copyright (C) 2010 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 (core::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 (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
364 i != m_media_fetch_threads.end(); i++)
368 void Client::connect(Address address)
370 DSTACK(__FUNCTION_NAME);
371 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
372 m_con.SetTimeoutMs(0);
373 m_con.Connect(address);
376 bool Client::connectedAndInitialized()
378 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
380 if(m_con.Connected() == false)
383 if(m_server_ser_ver == SER_FMT_VER_INVALID)
389 void Client::step(float dtime)
391 DSTACK(__FUNCTION_NAME);
397 if(m_ignore_damage_timer > dtime)
398 m_ignore_damage_timer -= dtime;
400 m_ignore_damage_timer = 0.0;
402 m_animation_time += dtime;
403 if(m_animation_time > 60.0)
404 m_animation_time -= 60.0;
406 m_time_of_day_update_timer += dtime;
408 //infostream<<"Client steps "<<dtime<<std::endl;
411 //TimeTaker timer("ReceiveAll()", m_device);
417 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
419 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
420 m_con.RunTimeouts(dtime);
427 float &counter = m_packetcounter_timer;
433 infostream<<"Client packetcounter (20s):"<<std::endl;
434 m_packetcounter.print(infostream);
435 m_packetcounter.clear();
439 // Get connection status
440 bool connected = connectedAndInitialized();
445 Delete unused sectors
447 NOTE: This jams the game for a while because deleting sectors
451 float &counter = m_delete_unused_sectors_timer;
459 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
461 core::list<v3s16> deleted_blocks;
463 float delete_unused_sectors_timeout =
464 g_settings->getFloat("client_delete_unused_sectors_timeout");
466 // Delete sector blocks
467 /*u32 num = m_env.getMap().unloadUnusedData
468 (delete_unused_sectors_timeout,
469 true, &deleted_blocks);*/
471 // Delete whole sectors
472 m_env.getMap().unloadUnusedData
473 (delete_unused_sectors_timeout,
476 if(deleted_blocks.size() > 0)
478 /*infostream<<"Client: Deleted blocks of "<<num
479 <<" unused sectors"<<std::endl;*/
480 /*infostream<<"Client: Deleted "<<num
481 <<" unused sectors"<<std::endl;*/
487 // Env is locked so con can be locked.
488 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
490 core::list<v3s16>::Iterator i = deleted_blocks.begin();
491 core::list<v3s16> sendlist;
494 if(sendlist.size() == 255 || i == deleted_blocks.end())
496 if(sendlist.size() == 0)
505 u32 replysize = 2+1+6*sendlist.size();
506 SharedBuffer<u8> reply(replysize);
507 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
508 reply[2] = sendlist.size();
510 for(core::list<v3s16>::Iterator
511 j = sendlist.begin();
512 j != sendlist.end(); j++)
514 writeV3S16(&reply[2+1+6*k], *j);
517 m_con.Send(PEER_ID_SERVER, 1, reply, true);
519 if(i == deleted_blocks.end())
525 sendlist.push_back(*i);
533 if(connected == false)
535 float &counter = m_connection_reinit_timer;
541 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
543 Player *myplayer = m_env.getLocalPlayer();
544 assert(myplayer != NULL);
546 // Send TOSERVER_INIT
547 // [0] u16 TOSERVER_INIT
548 // [2] u8 SER_FMT_VER_HIGHEST
549 // [3] u8[20] player_name
550 // [23] u8[28] password (new in some version)
551 // [51] u16 minimum supported network protocol version (added sometime)
552 // [53] u16 maximum supported network protocol version (added later than the previous one)
553 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
554 writeU16(&data[0], TOSERVER_INIT);
555 writeU8(&data[2], SER_FMT_VER_HIGHEST);
557 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
558 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
560 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
563 memset((char*)&data[23], 0, PASSWORD_SIZE);
564 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
566 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
567 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
569 // Send as unreliable
570 Send(0, data, false);
573 // Not connected, return
578 Do stuff if connected
582 Run Map's timers and unload unused data
584 const float map_timer_and_unload_dtime = 5.25;
585 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
587 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
588 core::list<v3s16> deleted_blocks;
589 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
590 g_settings->getFloat("client_unload_unused_data_timeout"),
593 /*if(deleted_blocks.size() > 0)
594 infostream<<"Client: Unloaded "<<deleted_blocks.size()
595 <<" unused blocks"<<std::endl;*/
599 NOTE: This loop is intentionally iterated the way it is.
602 core::list<v3s16>::Iterator i = deleted_blocks.begin();
603 core::list<v3s16> sendlist;
606 if(sendlist.size() == 255 || i == deleted_blocks.end())
608 if(sendlist.size() == 0)
617 u32 replysize = 2+1+6*sendlist.size();
618 SharedBuffer<u8> reply(replysize);
619 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
620 reply[2] = sendlist.size();
622 for(core::list<v3s16>::Iterator
623 j = sendlist.begin();
624 j != sendlist.end(); j++)
626 writeV3S16(&reply[2+1+6*k], *j);
629 m_con.Send(PEER_ID_SERVER, 1, reply, true);
631 if(i == deleted_blocks.end())
637 sendlist.push_back(*i);
647 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
649 // Control local player (0ms)
650 LocalPlayer *player = m_env.getLocalPlayer();
651 assert(player != NULL);
652 player->applyControl(dtime);
654 //TimeTaker envtimer("env step", m_device);
663 ClientEnvEvent event = m_env.getClientEvent();
664 if(event.type == CEE_NONE)
668 else if(event.type == CEE_PLAYER_DAMAGE)
670 if(m_ignore_damage_timer <= 0)
672 u8 damage = event.player_damage.amount;
674 if(event.player_damage.send_to_server)
677 // Add to ClientEvent queue
679 event.type = CE_PLAYER_DAMAGE;
680 event.player_damage.amount = damage;
681 m_client_event_queue.push_back(event);
691 float &counter = m_avg_rtt_timer;
696 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
697 // connectedAndInitialized() is true, peer exists.
698 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
699 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
704 Send player position to server
707 float &counter = m_playerpos_send_timer;
709 if(counter >= m_recommended_send_interval)
717 Replace updated meshes
720 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
722 //TimeTaker timer("** Processing mesh update result queue");
725 /*infostream<<"Mesh update result queue size is "
726 <<m_mesh_update_thread.m_queue_out.size()
729 int num_processed_meshes = 0;
730 while(m_mesh_update_thread.m_queue_out.size() > 0)
732 num_processed_meshes++;
733 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
734 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
737 //JMutexAutoLock lock(block->mesh_mutex);
739 // Delete the old mesh
740 if(block->mesh != NULL)
742 // TODO: Remove hardware buffers of meshbuffers of block->mesh
747 // Replace with the new mesh
748 block->mesh = r.mesh;
750 if(r.ack_block_to_server)
752 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
753 <<","<<r.p.Z<<")"<<std::endl;*/
764 u32 replysize = 2+1+6;
765 SharedBuffer<u8> reply(replysize);
766 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
768 writeV3S16(&reply[3], r.p);
770 m_con.Send(PEER_ID_SERVER, 1, reply, true);
773 if(num_processed_meshes > 0)
774 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
780 if (m_media_receive_started) {
781 bool all_stopped = true;
782 for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
783 thread != m_media_fetch_threads.end(); thread++) {
784 all_stopped &= !(*thread)->IsRunning();
785 while ((*thread)->m_file_data.size() > 0) {
786 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
787 ++m_media_received_count;
789 bool success = loadMedia(out.second, out.first);
791 verbosestream<<"Client: Loaded received media: "
792 <<"\""<<out.first<<"\". Caching."<<std::endl;
794 infostream<<"Client: Failed to load received media: "
795 <<"\""<<out.first<<"\". Not caching."<<std::endl;
799 bool did = fs::CreateAllDirs(getMediaCacheDir());
801 errorstream<<"Could not create media cache directory"
806 core::map<std::string, std::string>::Node *n;
807 n = m_media_name_sha1_map.find(out.first);
809 errorstream<<"The server sent a file that has not "
810 <<"been announced."<<std::endl;
812 m_media_cache.update_sha1(out.second);
817 core::list<MediaRequest> fetch_failed;
818 for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
819 thread != m_media_fetch_threads.end(); thread++) {
820 for (core::list<MediaRequest>::Iterator request = (*thread)->m_failed.begin();
821 request != (*thread)->m_failed.end(); request++)
822 fetch_failed.push_back(*request);
823 (*thread)->m_failed.clear();
825 if (fetch_failed.size() > 0) {
826 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
827 << "Requesting them the usual way." << std::endl;
828 request_media(fetch_failed);
834 If the server didn't update the inventory in a while, revert
835 the local inventory (so the player notices the lag problem
836 and knows something is wrong).
838 if(m_inventory_from_server)
840 float interval = 10.0;
841 float count_before = floor(m_inventory_from_server_age / interval);
843 m_inventory_from_server_age += dtime;
845 float count_after = floor(m_inventory_from_server_age / interval);
847 if(count_after != count_before)
849 // Do this every <interval> seconds after TOCLIENT_INVENTORY
850 // Reset the locally changed inventory to the authoritative inventory
851 Player *player = m_env.getLocalPlayer();
852 player->inventory = *m_inventory_from_server;
853 m_inventory_updated = true;
858 Update positions of sounds attached to objects
861 for(std::map<int, u16>::iterator
862 i = m_sounds_to_objects.begin();
863 i != m_sounds_to_objects.end(); i++)
865 int client_id = i->first;
866 u16 object_id = i->second;
867 ClientActiveObject *cao = m_env.getActiveObject(object_id);
870 v3f pos = cao->getPosition();
871 m_sound->updateSoundPosition(client_id, pos);
876 Handle removed remotely initiated sounds
878 m_removed_sounds_check_timer += dtime;
879 if(m_removed_sounds_check_timer >= 2.32)
881 m_removed_sounds_check_timer = 0;
882 // Find removed sounds and clear references to them
883 std::set<s32> removed_server_ids;
884 for(std::map<s32, int>::iterator
885 i = m_sounds_server_to_client.begin();
886 i != m_sounds_server_to_client.end();)
888 s32 server_id = i->first;
889 int client_id = i->second;
891 if(!m_sound->soundExists(client_id)){
892 m_sounds_server_to_client.erase(server_id);
893 m_sounds_client_to_server.erase(client_id);
894 m_sounds_to_objects.erase(client_id);
895 removed_server_ids.insert(server_id);
899 if(removed_server_ids.size() != 0)
901 std::ostringstream os(std::ios_base::binary);
902 writeU16(os, TOSERVER_REMOVED_SOUNDS);
903 writeU16(os, removed_server_ids.size());
904 for(std::set<s32>::iterator i = removed_server_ids.begin();
905 i != removed_server_ids.end(); i++)
907 std::string s = os.str();
908 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
915 bool Client::loadMedia(const std::string &data, const std::string &filename)
917 // Silly irrlicht's const-incorrectness
918 Buffer<char> data_rw(data.c_str(), data.size());
922 const char *image_ext[] = {
923 ".png", ".jpg", ".bmp", ".tga",
924 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
927 name = removeStringEnd(filename, image_ext);
930 verbosestream<<"Client: Attempting to load image "
931 <<"file \""<<filename<<"\""<<std::endl;
933 io::IFileSystem *irrfs = m_device->getFileSystem();
934 video::IVideoDriver *vdrv = m_device->getVideoDriver();
936 // Create an irrlicht memory file
937 io::IReadFile *rfile = irrfs->createMemoryReadFile(
938 *data_rw, data_rw.getSize(), "_tempreadfile");
941 video::IImage *img = vdrv->createImageFromFile(rfile);
943 errorstream<<"Client: Cannot create image from data of "
944 <<"file \""<<filename<<"\""<<std::endl;
949 m_tsrc->insertSourceImage(filename, img);
956 const char *sound_ext[] = {
957 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
958 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
961 name = removeStringEnd(filename, sound_ext);
964 verbosestream<<"Client: Attempting to load sound "
965 <<"file \""<<filename<<"\""<<std::endl;
966 m_sound->loadSoundData(name, data);
970 const char *model_ext[] = {
971 ".x", ".b3d", ".md2", ".obj",
974 name = removeStringEnd(filename, model_ext);
977 verbosestream<<"Client: Storing model into Irrlicht: "
978 <<"\""<<filename<<"\""<<std::endl;
980 io::IFileSystem *irrfs = m_device->getFileSystem();
981 io::IReadFile *rfile = irrfs->createMemoryReadFile(
982 *data_rw, data_rw.getSize(), filename.c_str());
985 scene::ISceneManager *smgr = m_device->getSceneManager();
986 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
987 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
992 errorstream<<"Client: Don't know how to load file \""
993 <<filename<<"\""<<std::endl;
997 // Virtual methods from con::PeerHandler
998 void Client::peerAdded(con::Peer *peer)
1000 infostream<<"Client::peerAdded(): peer->id="
1001 <<peer->id<<std::endl;
1003 void Client::deletingPeer(con::Peer *peer, bool timeout)
1005 infostream<<"Client::deletingPeer(): "
1006 "Server Peer is getting deleted "
1007 <<"(timeout="<<timeout<<")"<<std::endl;
1012 u16 number of files requested
1018 void Client::request_media(const core::list<MediaRequest> &file_requests)
1020 std::ostringstream os(std::ios_base::binary);
1021 writeU16(os, TOSERVER_REQUEST_MEDIA);
1022 writeU16(os, file_requests.size());
1024 for(core::list<MediaRequest>::ConstIterator i = file_requests.begin();
1025 i != file_requests.end(); i++) {
1026 os<<serializeString(i->name);
1030 std::string s = os.str();
1031 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1033 Send(0, data, true);
1034 infostream<<"Client: Sending media request list to server ("
1035 <<file_requests.size()<<" files)"<<std::endl;
1038 void Client::ReceiveAll()
1040 DSTACK(__FUNCTION_NAME);
1041 u32 start_ms = porting::getTimeMs();
1044 // Limit time even if there would be huge amounts of data to
1046 if(porting::getTimeMs() > start_ms + 100)
1051 g_profiler->graphAdd("client_received_packets", 1);
1053 catch(con::NoIncomingDataException &e)
1057 catch(con::InvalidIncomingDataException &e)
1059 infostream<<"Client::ReceiveAll(): "
1060 "InvalidIncomingDataException: what()="
1061 <<e.what()<<std::endl;
1066 void Client::Receive()
1068 DSTACK(__FUNCTION_NAME);
1069 SharedBuffer<u8> data;
1073 //TimeTaker t1("con mutex and receive", m_device);
1074 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1075 datasize = m_con.Receive(sender_peer_id, data);
1077 //TimeTaker t1("ProcessData", m_device);
1078 ProcessData(*data, datasize, sender_peer_id);
1082 sender_peer_id given to this shall be quaranteed to be a valid peer
1084 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1086 DSTACK(__FUNCTION_NAME);
1088 // Ignore packets that don't even fit a command
1091 m_packetcounter.add(60000);
1095 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1097 //infostream<<"Client: received command="<<command<<std::endl;
1098 m_packetcounter.add((u16)command);
1101 If this check is removed, be sure to change the queue
1102 system to know the ids
1104 if(sender_peer_id != PEER_ID_SERVER)
1106 infostream<<"Client::ProcessData(): Discarding data not "
1107 "coming from server: peer_id="<<sender_peer_id
1112 u8 ser_version = m_server_ser_ver;
1114 //infostream<<"Client received command="<<(int)command<<std::endl;
1116 if(command == TOCLIENT_INIT)
1121 u8 deployed = data[2];
1123 infostream<<"Client: TOCLIENT_INIT received with "
1124 "deployed="<<((int)deployed&0xff)<<std::endl;
1126 if(deployed < SER_FMT_VER_LOWEST
1127 || deployed > SER_FMT_VER_HIGHEST)
1129 infostream<<"Client: TOCLIENT_INIT: Server sent "
1130 <<"unsupported ser_fmt_ver"<<std::endl;
1134 m_server_ser_ver = deployed;
1136 // Get player position
1137 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1138 if(datasize >= 2+1+6)
1139 playerpos_s16 = readV3S16(&data[2+1]);
1140 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1143 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1145 // Set player position
1146 Player *player = m_env.getLocalPlayer();
1147 assert(player != NULL);
1148 player->setPosition(playerpos_f);
1151 if(datasize >= 2+1+6+8)
1154 m_map_seed = readU64(&data[2+1+6]);
1155 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1158 if(datasize >= 2+1+6+8+4)
1161 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1162 infostream<<"Client: received recommended send interval "
1163 <<m_recommended_send_interval<<std::endl;
1168 SharedBuffer<u8> reply(replysize);
1169 writeU16(&reply[0], TOSERVER_INIT2);
1171 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1176 if(command == TOCLIENT_ACCESS_DENIED)
1178 // The server didn't like our password. Note, this needs
1179 // to be processed even if the serialisation format has
1180 // not been agreed yet, the same as TOCLIENT_INIT.
1181 m_access_denied = true;
1182 m_access_denied_reason = L"Unknown";
1185 std::string datastring((char*)&data[2], datasize-2);
1186 std::istringstream is(datastring, std::ios_base::binary);
1187 m_access_denied_reason = deSerializeWideString(is);
1192 if(ser_version == SER_FMT_VER_INVALID)
1194 infostream<<"Client: Server serialization"
1195 " format invalid or not initialized."
1196 " Skipping incoming command="<<command<<std::endl;
1200 // Just here to avoid putting the two if's together when
1201 // making some copypasta
1204 if(command == TOCLIENT_REMOVENODE)
1209 p.X = readS16(&data[2]);
1210 p.Y = readS16(&data[4]);
1211 p.Z = readS16(&data[6]);
1213 //TimeTaker t1("TOCLIENT_REMOVENODE");
1217 else if(command == TOCLIENT_ADDNODE)
1219 if(datasize < 8 + MapNode::serializedLength(ser_version))
1223 p.X = readS16(&data[2]);
1224 p.Y = readS16(&data[4]);
1225 p.Z = readS16(&data[6]);
1227 //TimeTaker t1("TOCLIENT_ADDNODE");
1230 n.deSerialize(&data[8], ser_version);
1234 else if(command == TOCLIENT_BLOCKDATA)
1236 // Ignore too small packet
1241 p.X = readS16(&data[2]);
1242 p.Y = readS16(&data[4]);
1243 p.Z = readS16(&data[6]);
1245 /*infostream<<"Client: Thread: BLOCKDATA for ("
1246 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1247 /*infostream<<"Client: Thread: BLOCKDATA for ("
1248 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1250 std::string datastring((char*)&data[8], datasize-8);
1251 std::istringstream istr(datastring, std::ios_base::binary);
1256 v2s16 p2d(p.X, p.Z);
1257 sector = m_env.getMap().emergeSector(p2d);
1259 assert(sector->getPos() == p2d);
1261 //TimeTaker timer("MapBlock deSerialize");
1264 block = sector->getBlockNoCreateNoEx(p.Y);
1268 Update an existing block
1270 //infostream<<"Updating"<<std::endl;
1271 block->deSerialize(istr, ser_version, false);
1278 //infostream<<"Creating new"<<std::endl;
1279 block = new MapBlock(&m_env.getMap(), p, this);
1280 block->deSerialize(istr, ser_version, false);
1281 sector->insertBlock(block);
1295 u32 replysize = 2+1+6;
1296 SharedBuffer<u8> reply(replysize);
1297 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1299 writeV3S16(&reply[3], p);
1301 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1305 Add it to mesh update queue and set it to be acknowledged after update.
1307 //infostream<<"Adding mesh update task for received block"<<std::endl;
1308 addUpdateMeshTaskWithEdge(p, true);
1310 else if(command == TOCLIENT_INVENTORY)
1315 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1318 //TimeTaker t2("mutex locking", m_device);
1319 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1322 //TimeTaker t3("istringstream init", m_device);
1323 std::string datastring((char*)&data[2], datasize-2);
1324 std::istringstream is(datastring, std::ios_base::binary);
1327 //m_env.printPlayers(infostream);
1329 //TimeTaker t4("player get", m_device);
1330 Player *player = m_env.getLocalPlayer();
1331 assert(player != NULL);
1334 //TimeTaker t1("inventory.deSerialize()", m_device);
1335 player->inventory.deSerialize(is);
1338 m_inventory_updated = true;
1340 delete m_inventory_from_server;
1341 m_inventory_from_server = new Inventory(player->inventory);
1342 m_inventory_from_server_age = 0.0;
1344 //infostream<<"Client got player inventory:"<<std::endl;
1345 //player->inventory.print(infostream);
1348 else if(command == TOCLIENT_TIME_OF_DAY)
1353 u16 time_of_day = readU16(&data[2]);
1354 time_of_day = time_of_day % 24000;
1355 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1356 float time_speed = 0;
1357 if(datasize >= 2 + 2 + 4){
1358 time_speed = readF1000(&data[4]);
1360 // Old message; try to approximate speed of time by ourselves
1361 float time_of_day_f = (float)time_of_day / 24000.0;
1362 float tod_diff_f = 0;
1363 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1364 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1366 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1367 m_last_time_of_day_f = time_of_day_f;
1368 float time_diff = m_time_of_day_update_timer;
1369 m_time_of_day_update_timer = 0;
1370 if(m_time_of_day_set){
1371 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1372 infostream<<"Client: Measured time_of_day speed (old format): "
1373 <<time_speed<<" tod_diff_f="<<tod_diff_f
1374 <<" time_diff="<<time_diff<<std::endl;
1378 // Update environment
1379 m_env.setTimeOfDay(time_of_day);
1380 m_env.setTimeOfDaySpeed(time_speed);
1381 m_time_of_day_set = true;
1383 u32 dr = m_env.getDayNightRatio();
1384 verbosestream<<"Client: time_of_day="<<time_of_day
1385 <<" time_speed="<<time_speed
1386 <<" dr="<<dr<<std::endl;
1388 else if(command == TOCLIENT_CHAT_MESSAGE)
1396 std::string datastring((char*)&data[2], datasize-2);
1397 std::istringstream is(datastring, std::ios_base::binary);
1400 is.read((char*)buf, 2);
1401 u16 len = readU16(buf);
1403 std::wstring message;
1404 for(u16 i=0; i<len; i++)
1406 is.read((char*)buf, 2);
1407 message += (wchar_t)readU16(buf);
1410 /*infostream<<"Client received chat message: "
1411 <<wide_to_narrow(message)<<std::endl;*/
1413 m_chat_queue.push_back(message);
1415 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1417 //if(g_settings->getBool("enable_experimental"))
1421 u16 count of removed objects
1422 for all removed objects {
1425 u16 count of added objects
1426 for all added objects {
1429 u32 initialization data length
1430 string initialization data
1435 // Get all data except the command number
1436 std::string datastring((char*)&data[2], datasize-2);
1437 // Throw them in an istringstream
1438 std::istringstream is(datastring, std::ios_base::binary);
1442 // Read removed objects
1444 u16 removed_count = readU16((u8*)buf);
1445 for(u16 i=0; i<removed_count; i++)
1448 u16 id = readU16((u8*)buf);
1451 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1452 m_env.removeActiveObject(id);
1456 // Read added objects
1458 u16 added_count = readU16((u8*)buf);
1459 for(u16 i=0; i<added_count; i++)
1462 u16 id = readU16((u8*)buf);
1464 u8 type = readU8((u8*)buf);
1465 std::string data = deSerializeLongString(is);
1468 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1469 m_env.addActiveObject(id, type, data);
1474 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1476 //if(g_settings->getBool("enable_experimental"))
1488 // Get all data except the command number
1489 std::string datastring((char*)&data[2], datasize-2);
1490 // Throw them in an istringstream
1491 std::istringstream is(datastring, std::ios_base::binary);
1493 while(is.eof() == false)
1497 u16 id = readU16((u8*)buf);
1501 u16 message_size = readU16((u8*)buf);
1502 std::string message;
1503 message.reserve(message_size);
1504 for(u16 i=0; i<message_size; i++)
1507 message.append(buf, 1);
1509 // Pass on to the environment
1511 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1512 m_env.processActiveObjectMessage(id, message);
1517 else if(command == TOCLIENT_HP)
1519 std::string datastring((char*)&data[2], datasize-2);
1520 std::istringstream is(datastring, std::ios_base::binary);
1521 Player *player = m_env.getLocalPlayer();
1522 assert(player != NULL);
1523 u8 oldhp = player->hp;
1529 // Add to ClientEvent queue
1531 event.type = CE_PLAYER_DAMAGE;
1532 event.player_damage.amount = oldhp - hp;
1533 m_client_event_queue.push_back(event);
1536 else if(command == TOCLIENT_MOVE_PLAYER)
1538 std::string datastring((char*)&data[2], datasize-2);
1539 std::istringstream is(datastring, std::ios_base::binary);
1540 Player *player = m_env.getLocalPlayer();
1541 assert(player != NULL);
1542 v3f pos = readV3F1000(is);
1543 f32 pitch = readF1000(is);
1544 f32 yaw = readF1000(is);
1545 player->setPosition(pos);
1546 /*player->setPitch(pitch);
1547 player->setYaw(yaw);*/
1549 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1550 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1556 Add to ClientEvent queue.
1557 This has to be sent to the main program because otherwise
1558 it would just force the pitch and yaw values to whatever
1559 the camera points to.
1562 event.type = CE_PLAYER_FORCE_MOVE;
1563 event.player_force_move.pitch = pitch;
1564 event.player_force_move.yaw = yaw;
1565 m_client_event_queue.push_back(event);
1567 // Ignore damage for a few seconds, so that the player doesn't
1568 // get damage from falling on ground
1569 m_ignore_damage_timer = 3.0;
1571 else if(command == TOCLIENT_PLAYERITEM)
1573 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1575 else if(command == TOCLIENT_DEATHSCREEN)
1577 std::string datastring((char*)&data[2], datasize-2);
1578 std::istringstream is(datastring, std::ios_base::binary);
1580 bool set_camera_point_target = readU8(is);
1581 v3f camera_point_target = readV3F1000(is);
1584 event.type = CE_DEATHSCREEN;
1585 event.deathscreen.set_camera_point_target = set_camera_point_target;
1586 event.deathscreen.camera_point_target_x = camera_point_target.X;
1587 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1588 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1589 m_client_event_queue.push_back(event);
1591 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1593 std::string datastring((char*)&data[2], datasize-2);
1594 std::istringstream is(datastring, std::ios_base::binary);
1596 // Mesh update thread must be stopped while
1597 // updating content definitions
1598 assert(!m_mesh_update_thread.IsRunning());
1600 int num_files = readU16(is);
1602 infostream<<"Client: Received media announcement: packet size: "
1603 <<datasize<<std::endl;
1605 core::list<MediaRequest> file_requests;
1607 for(int i=0; i<num_files; i++)
1609 //read file from cache
1610 std::string name = deSerializeString(is);
1611 std::string sha1_base64 = deSerializeString(is);
1613 // if name contains illegal characters, ignore the file
1614 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1615 errorstream<<"Client: ignoring illegal file name "
1616 <<"sent by server: \""<<name<<"\""<<std::endl;
1620 std::string sha1_raw = base64_decode(sha1_base64);
1621 std::string sha1_hex = hex_encode(sha1_raw);
1622 std::ostringstream tmp_os(std::ios_base::binary);
1623 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1624 m_media_name_sha1_map.set(name, sha1_raw);
1626 // If found in cache, try to load it from there
1629 bool success = loadMedia(tmp_os.str(), name);
1631 verbosestream<<"Client: Loaded cached media: "
1632 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1635 infostream<<"Client: Failed to load cached media: "
1636 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1639 // Didn't load from cache; queue it to be requested
1640 verbosestream<<"Client: Adding file to request list: \""
1641 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1642 file_requests.push_back(MediaRequest(name));
1645 std::string remote_media = "";
1647 remote_media = deSerializeString(is);
1649 catch(SerializationError) {
1650 // not supported by server or turned off
1653 m_media_count = file_requests.size();
1654 m_media_receive_started = true;
1656 if (remote_media == "" || !USE_CURL) {
1657 request_media(file_requests);
1660 core::list<MediaFetchThread*>::Iterator cur = m_media_fetch_threads.begin();
1661 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1662 i != file_requests.end(); i++) {
1663 (*cur)->m_file_requests.push_back(*i);
1665 if (cur == m_media_fetch_threads.end())
1666 cur = m_media_fetch_threads.begin();
1668 for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
1669 i != m_media_fetch_threads.end(); i++) {
1670 (*i)->m_remote_url = remote_media;
1675 // notify server we received everything
1676 std::ostringstream os(std::ios_base::binary);
1677 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1678 std::string s = os.str();
1679 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1681 Send(0, data, true);
1684 event.type = CE_TEXTURES_UPDATED;
1685 m_client_event_queue.push_back(event);
1687 else if(command == TOCLIENT_MEDIA)
1689 if (m_media_count == 0)
1691 std::string datastring((char*)&data[2], datasize-2);
1692 std::istringstream is(datastring, std::ios_base::binary);
1694 // Mesh update thread must be stopped while
1695 // updating content definitions
1696 assert(!m_mesh_update_thread.IsRunning());
1700 u16 total number of file bunches
1701 u16 index of this bunch
1702 u32 number of files in this bunch
1710 int num_bunches = readU16(is);
1711 int bunch_i = readU16(is);
1712 int num_files = readU32(is);
1713 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1714 <<num_bunches<<" files="<<num_files
1715 <<" size="<<datasize<<std::endl;
1716 for(int i=0; i<num_files; i++){
1717 m_media_received_count++;
1718 std::string name = deSerializeString(is);
1719 std::string data = deSerializeLongString(is);
1721 // if name contains illegal characters, ignore the file
1722 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1723 errorstream<<"Client: ignoring illegal file name "
1724 <<"sent by server: \""<<name<<"\""<<std::endl;
1728 bool success = loadMedia(data, name);
1730 verbosestream<<"Client: Loaded received media: "
1731 <<"\""<<name<<"\". Caching."<<std::endl;
1733 infostream<<"Client: Failed to load received media: "
1734 <<"\""<<name<<"\". Not caching."<<std::endl;
1738 bool did = fs::CreateAllDirs(getMediaCacheDir());
1740 errorstream<<"Could not create media cache directory"
1745 core::map<std::string, std::string>::Node *n;
1746 n = m_media_name_sha1_map.find(name);
1748 errorstream<<"The server sent a file that has not "
1749 <<"been announced."<<std::endl;
1751 m_media_cache.update_sha1(data);
1756 event.type = CE_TEXTURES_UPDATED;
1757 m_client_event_queue.push_back(event);
1759 else if(command == TOCLIENT_TOOLDEF)
1761 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1763 else if(command == TOCLIENT_NODEDEF)
1765 infostream<<"Client: Received node definitions: packet size: "
1766 <<datasize<<std::endl;
1768 // Mesh update thread must be stopped while
1769 // updating content definitions
1770 assert(!m_mesh_update_thread.IsRunning());
1772 // Decompress node definitions
1773 std::string datastring((char*)&data[2], datasize-2);
1774 std::istringstream is(datastring, std::ios_base::binary);
1775 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1776 std::ostringstream tmp_os;
1777 decompressZlib(tmp_is, tmp_os);
1779 // Deserialize node definitions
1780 std::istringstream tmp_is2(tmp_os.str());
1781 m_nodedef->deSerialize(tmp_is2);
1782 m_nodedef_received = true;
1784 else if(command == TOCLIENT_CRAFTITEMDEF)
1786 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1788 else if(command == TOCLIENT_ITEMDEF)
1790 infostream<<"Client: Received item definitions: packet size: "
1791 <<datasize<<std::endl;
1793 // Mesh update thread must be stopped while
1794 // updating content definitions
1795 assert(!m_mesh_update_thread.IsRunning());
1797 // Decompress item definitions
1798 std::string datastring((char*)&data[2], datasize-2);
1799 std::istringstream is(datastring, std::ios_base::binary);
1800 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1801 std::ostringstream tmp_os;
1802 decompressZlib(tmp_is, tmp_os);
1804 // Deserialize node definitions
1805 std::istringstream tmp_is2(tmp_os.str());
1806 m_itemdef->deSerialize(tmp_is2);
1807 m_itemdef_received = true;
1809 else if(command == TOCLIENT_PLAY_SOUND)
1811 std::string datastring((char*)&data[2], datasize-2);
1812 std::istringstream is(datastring, std::ios_base::binary);
1814 s32 server_id = readS32(is);
1815 std::string name = deSerializeString(is);
1816 float gain = readF1000(is);
1817 int type = readU8(is); // 0=local, 1=positional, 2=object
1818 v3f pos = readV3F1000(is);
1819 u16 object_id = readU16(is);
1820 bool loop = readU8(is);
1825 client_id = m_sound->playSound(name, loop, gain);
1827 case 1: // positional
1828 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1831 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1833 pos = cao->getPosition();
1834 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1835 // TODO: Set up sound to move with object
1840 if(client_id != -1){
1841 m_sounds_server_to_client[server_id] = client_id;
1842 m_sounds_client_to_server[client_id] = server_id;
1844 m_sounds_to_objects[client_id] = object_id;
1847 else if(command == TOCLIENT_STOP_SOUND)
1849 std::string datastring((char*)&data[2], datasize-2);
1850 std::istringstream is(datastring, std::ios_base::binary);
1852 s32 server_id = readS32(is);
1853 std::map<s32, int>::iterator i =
1854 m_sounds_server_to_client.find(server_id);
1855 if(i != m_sounds_server_to_client.end()){
1856 int client_id = i->second;
1857 m_sound->stopSound(client_id);
1860 else if(command == TOCLIENT_PRIVILEGES)
1862 std::string datastring((char*)&data[2], datasize-2);
1863 std::istringstream is(datastring, std::ios_base::binary);
1865 m_privileges.clear();
1866 infostream<<"Client: Privileges updated: ";
1867 u16 num_privileges = readU16(is);
1868 for(u16 i=0; i<num_privileges; i++){
1869 std::string priv = deSerializeString(is);
1870 m_privileges.insert(priv);
1871 infostream<<priv<<" ";
1873 infostream<<std::endl;
1875 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1877 std::string datastring((char*)&data[2], datasize-2);
1878 std::istringstream is(datastring, std::ios_base::binary);
1880 // Store formspec in LocalPlayer
1881 Player *player = m_env.getLocalPlayer();
1882 assert(player != NULL);
1883 player->inventory_formspec = deSerializeLongString(is);
1885 else if(command == TOCLIENT_DETACHED_INVENTORY)
1887 std::string datastring((char*)&data[2], datasize-2);
1888 std::istringstream is(datastring, std::ios_base::binary);
1890 std::string name = deSerializeString(is);
1892 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1894 Inventory *inv = NULL;
1895 if(m_detached_inventories.count(name) > 0)
1896 inv = m_detached_inventories[name];
1898 inv = new Inventory(m_itemdef);
1899 m_detached_inventories[name] = inv;
1901 inv->deSerialize(is);
1905 infostream<<"Client: Ignoring unknown command "
1906 <<command<<std::endl;
1910 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1912 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1913 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1916 void Client::interact(u8 action, const PointedThing& pointed)
1918 if(connectedAndInitialized() == false){
1919 infostream<<"Client::interact() "
1920 "cancelled (not connected)"
1925 std::ostringstream os(std::ios_base::binary);
1931 [5] u32 length of the next item
1932 [9] serialized PointedThing
1934 0: start digging (from undersurface) or use
1935 1: stop digging (all parameters ignored)
1936 2: digging completed
1937 3: place block or item (to abovesurface)
1940 writeU16(os, TOSERVER_INTERACT);
1941 writeU8(os, action);
1942 writeU16(os, getPlayerItem());
1943 std::ostringstream tmp_os(std::ios::binary);
1944 pointed.serialize(tmp_os);
1945 os<<serializeLongString(tmp_os.str());
1947 std::string s = os.str();
1948 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1951 Send(0, data, true);
1954 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1955 const std::map<std::string, std::string> &fields)
1957 std::ostringstream os(std::ios_base::binary);
1959 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1961 os<<serializeString(formname);
1962 writeU16(os, fields.size());
1963 for(std::map<std::string, std::string>::const_iterator
1964 i = fields.begin(); i != fields.end(); i++){
1965 const std::string &name = i->first;
1966 const std::string &value = i->second;
1967 os<<serializeString(name);
1968 os<<serializeLongString(value);
1972 std::string s = os.str();
1973 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1975 Send(0, data, true);
1978 void Client::sendInventoryFields(const std::string &formname,
1979 const std::map<std::string, std::string> &fields)
1981 std::ostringstream os(std::ios_base::binary);
1983 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1984 os<<serializeString(formname);
1985 writeU16(os, fields.size());
1986 for(std::map<std::string, std::string>::const_iterator
1987 i = fields.begin(); i != fields.end(); i++){
1988 const std::string &name = i->first;
1989 const std::string &value = i->second;
1990 os<<serializeString(name);
1991 os<<serializeLongString(value);
1995 std::string s = os.str();
1996 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1998 Send(0, data, true);
2001 void Client::sendInventoryAction(InventoryAction *a)
2003 std::ostringstream os(std::ios_base::binary);
2007 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2008 os.write((char*)buf, 2);
2013 std::string s = os.str();
2014 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2016 Send(0, data, true);
2019 void Client::sendChatMessage(const std::wstring &message)
2021 std::ostringstream os(std::ios_base::binary);
2025 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2026 os.write((char*)buf, 2);
2029 writeU16(buf, message.size());
2030 os.write((char*)buf, 2);
2033 for(u32 i=0; i<message.size(); i++)
2037 os.write((char*)buf, 2);
2041 std::string s = os.str();
2042 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2044 Send(0, data, true);
2047 void Client::sendChangePassword(const std::wstring oldpassword,
2048 const std::wstring newpassword)
2050 Player *player = m_env.getLocalPlayer();
2054 std::string playername = player->getName();
2055 std::string oldpwd = translatePassword(playername, oldpassword);
2056 std::string newpwd = translatePassword(playername, newpassword);
2058 std::ostringstream os(std::ios_base::binary);
2059 u8 buf[2+PASSWORD_SIZE*2];
2061 [0] u16 TOSERVER_PASSWORD
2062 [2] u8[28] old password
2063 [30] u8[28] new password
2066 writeU16(buf, TOSERVER_PASSWORD);
2067 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2069 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2070 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2072 buf[2+PASSWORD_SIZE-1] = 0;
2073 buf[30+PASSWORD_SIZE-1] = 0;
2074 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2077 std::string s = os.str();
2078 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2080 Send(0, data, true);
2084 void Client::sendDamage(u8 damage)
2086 DSTACK(__FUNCTION_NAME);
2087 std::ostringstream os(std::ios_base::binary);
2089 writeU16(os, TOSERVER_DAMAGE);
2090 writeU8(os, damage);
2093 std::string s = os.str();
2094 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2096 Send(0, data, true);
2099 void Client::sendRespawn()
2101 DSTACK(__FUNCTION_NAME);
2102 std::ostringstream os(std::ios_base::binary);
2104 writeU16(os, TOSERVER_RESPAWN);
2107 std::string s = os.str();
2108 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2110 Send(0, data, true);
2113 void Client::sendPlayerPos()
2115 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2117 LocalPlayer *myplayer = m_env.getLocalPlayer();
2118 if(myplayer == NULL)
2121 // Save bandwidth by only updating position when something changed
2122 if(myplayer->last_position == myplayer->getPosition() &&
2123 myplayer->last_speed == myplayer->getSpeed() &&
2124 myplayer->last_pitch == myplayer->getPitch() &&
2125 myplayer->last_yaw == myplayer->getYaw() &&
2126 myplayer->last_keyPressed == myplayer->keyPressed)
2129 myplayer->last_position = myplayer->getPosition();
2130 myplayer->last_speed = myplayer->getSpeed();
2131 myplayer->last_pitch = myplayer->getPitch();
2132 myplayer->last_yaw = myplayer->getYaw();
2133 myplayer->last_keyPressed = myplayer->keyPressed;
2137 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2138 our_peer_id = m_con.GetPeerID();
2141 // Set peer id if not set already
2142 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2143 myplayer->peer_id = our_peer_id;
2144 // Check that an existing peer_id is the same as the connection's
2145 assert(myplayer->peer_id == our_peer_id);
2147 v3f pf = myplayer->getPosition();
2148 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2149 v3f sf = myplayer->getSpeed();
2150 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2151 s32 pitch = myplayer->getPitch() * 100;
2152 s32 yaw = myplayer->getYaw() * 100;
2153 u32 keyPressed=myplayer->keyPressed;
2157 [2] v3s32 position*100
2158 [2+12] v3s32 speed*100
2159 [2+12+12] s32 pitch*100
2160 [2+12+12+4] s32 yaw*100
2161 [2+12+12+4+4] u32 keyPressed
2163 SharedBuffer<u8> data(2+12+12+4+4+4);
2164 writeU16(&data[0], TOSERVER_PLAYERPOS);
2165 writeV3S32(&data[2], position);
2166 writeV3S32(&data[2+12], speed);
2167 writeS32(&data[2+12+12], pitch);
2168 writeS32(&data[2+12+12+4], yaw);
2169 writeU32(&data[2+12+12+4+4], keyPressed);
2170 // Send as unreliable
2171 Send(0, data, false);
2174 void Client::sendPlayerItem(u16 item)
2176 Player *myplayer = m_env.getLocalPlayer();
2177 if(myplayer == NULL)
2180 u16 our_peer_id = m_con.GetPeerID();
2182 // Set peer id if not set already
2183 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2184 myplayer->peer_id = our_peer_id;
2185 // Check that an existing peer_id is the same as the connection's
2186 assert(myplayer->peer_id == our_peer_id);
2188 SharedBuffer<u8> data(2+2);
2189 writeU16(&data[0], TOSERVER_PLAYERITEM);
2190 writeU16(&data[2], item);
2193 Send(0, data, true);
2196 void Client::removeNode(v3s16 p)
2198 core::map<v3s16, MapBlock*> modified_blocks;
2202 //TimeTaker t("removeNodeAndUpdate", m_device);
2203 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2205 catch(InvalidPositionException &e)
2209 // add urgent task to update the modified node
2210 addUpdateMeshTaskForNode(p, false, true);
2212 for(core::map<v3s16, MapBlock * >::Iterator
2213 i = modified_blocks.getIterator();
2214 i.atEnd() == false; i++)
2216 v3s16 p = i.getNode()->getKey();
2217 addUpdateMeshTaskWithEdge(p);
2221 void Client::addNode(v3s16 p, MapNode n)
2223 TimeTaker timer1("Client::addNode()");
2225 core::map<v3s16, MapBlock*> modified_blocks;
2229 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2230 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2232 catch(InvalidPositionException &e)
2235 for(core::map<v3s16, MapBlock * >::Iterator
2236 i = modified_blocks.getIterator();
2237 i.atEnd() == false; i++)
2239 v3s16 p = i.getNode()->getKey();
2240 addUpdateMeshTaskWithEdge(p);
2244 void Client::setPlayerControl(PlayerControl &control)
2246 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2247 LocalPlayer *player = m_env.getLocalPlayer();
2248 assert(player != NULL);
2249 player->control = control;
2252 void Client::selectPlayerItem(u16 item)
2254 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2255 m_playeritem = item;
2256 m_inventory_updated = true;
2257 sendPlayerItem(item);
2260 // Returns true if the inventory of the local player has been
2261 // updated from the server. If it is true, it is set to false.
2262 bool Client::getLocalInventoryUpdated()
2264 // m_inventory_updated is behind envlock
2265 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2266 bool updated = m_inventory_updated;
2267 m_inventory_updated = false;
2271 // Copies the inventory of the local player to parameter
2272 void Client::getLocalInventory(Inventory &dst)
2274 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2275 Player *player = m_env.getLocalPlayer();
2276 assert(player != NULL);
2277 dst = player->inventory;
2280 Inventory* Client::getInventory(const InventoryLocation &loc)
2283 case InventoryLocation::UNDEFINED:
2286 case InventoryLocation::CURRENT_PLAYER:
2288 Player *player = m_env.getLocalPlayer();
2289 assert(player != NULL);
2290 return &player->inventory;
2293 case InventoryLocation::PLAYER:
2295 Player *player = m_env.getPlayer(loc.name.c_str());
2298 return &player->inventory;
2301 case InventoryLocation::NODEMETA:
2303 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2306 return meta->getInventory();
2309 case InventoryLocation::DETACHED:
2311 if(m_detached_inventories.count(loc.name) == 0)
2313 return m_detached_inventories[loc.name];
2321 void Client::inventoryAction(InventoryAction *a)
2324 Send it to the server
2326 sendInventoryAction(a);
2329 Predict some local inventory changes
2331 a->clientApply(this, this);
2334 ClientActiveObject * Client::getSelectedActiveObject(
2336 v3f from_pos_f_on_map,
2337 core::line3d<f32> shootline_on_map
2340 core::array<DistanceSortedActiveObject> objects;
2342 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2344 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2347 // After this, the closest object is the first in the array.
2350 for(u32 i=0; i<objects.size(); i++)
2352 ClientActiveObject *obj = objects[i].obj;
2354 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2355 if(selection_box == NULL)
2358 v3f pos = obj->getPosition();
2360 core::aabbox3d<f32> offsetted_box(
2361 selection_box->MinEdge + pos,
2362 selection_box->MaxEdge + pos
2365 if(offsetted_box.intersectsWithLine(shootline_on_map))
2367 //infostream<<"Returning selected object"<<std::endl;
2372 //infostream<<"No object selected; returning NULL."<<std::endl;
2376 void Client::printDebugInfo(std::ostream &os)
2378 //JMutexAutoLock lock1(m_fetchblock_mutex);
2379 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2381 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2382 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2383 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2387 core::list<std::wstring> Client::getConnectedPlayerNames()
2389 core::list<Player*> players = m_env.getPlayers(true);
2390 core::list<std::wstring> playerNames;
2391 for(core::list<Player*>::Iterator
2392 i = players.begin();
2393 i != players.end(); i++)
2395 Player *player = *i;
2396 playerNames.push_back(narrow_to_wide(player->getName()));
2401 float Client::getAnimationTime()
2403 return m_animation_time;
2406 int Client::getCrackLevel()
2408 return m_crack_level;
2411 void Client::setCrack(int level, v3s16 pos)
2413 int old_crack_level = m_crack_level;
2414 v3s16 old_crack_pos = m_crack_pos;
2416 m_crack_level = level;
2419 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2422 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2424 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2427 addUpdateMeshTaskForNode(pos, false, true);
2433 Player *player = m_env.getLocalPlayer();
2434 assert(player != NULL);
2438 bool Client::getChatMessage(std::wstring &message)
2440 if(m_chat_queue.size() == 0)
2442 message = m_chat_queue.pop_front();
2446 void Client::typeChatMessage(const std::wstring &message)
2448 // Discard empty line
2453 sendChatMessage(message);
2456 if (message[0] == L'/')
2458 m_chat_queue.push_back(
2459 (std::wstring)L"issued command: "+message);
2463 LocalPlayer *player = m_env.getLocalPlayer();
2464 assert(player != NULL);
2465 std::wstring name = narrow_to_wide(player->getName());
2466 m_chat_queue.push_back(
2467 (std::wstring)L"<"+name+L"> "+message);
2471 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2473 /*infostream<<"Client::addUpdateMeshTask(): "
2474 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2475 <<" ack_to_server="<<ack_to_server
2476 <<" urgent="<<urgent
2479 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2484 Create a task to update the mesh of the block
2487 MeshMakeData *data = new MeshMakeData(this);
2490 //TimeTaker timer("data fill");
2492 // Debug: 1-6ms, avg=2ms
2494 data->setCrack(m_crack_level, m_crack_pos);
2495 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2499 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2501 // Add task to queue
2502 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2504 /*infostream<<"Mesh update input queue size is "
2505 <<m_mesh_update_thread.m_queue_in.size()
2509 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2513 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2514 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2519 v3s16 p = blockpos + v3s16(0,0,0);
2520 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2521 addUpdateMeshTask(p, ack_to_server, urgent);
2523 catch(InvalidPositionException &e){}
2526 v3s16 p = blockpos + v3s16(-1,0,0);
2527 addUpdateMeshTask(p, false, urgent);
2529 catch(InvalidPositionException &e){}
2531 v3s16 p = blockpos + v3s16(0,-1,0);
2532 addUpdateMeshTask(p, false, urgent);
2534 catch(InvalidPositionException &e){}
2536 v3s16 p = blockpos + v3s16(0,0,-1);
2537 addUpdateMeshTask(p, false, urgent);
2539 catch(InvalidPositionException &e){}
2542 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2546 infostream<<"Client::addUpdateMeshTaskForNode(): "
2547 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2551 v3s16 blockpos = getNodeBlockPos(nodepos);
2552 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2555 v3s16 p = blockpos + v3s16(0,0,0);
2556 addUpdateMeshTask(p, ack_to_server, urgent);
2558 catch(InvalidPositionException &e){}
2560 if(nodepos.X == blockpos_relative.X){
2562 v3s16 p = blockpos + v3s16(-1,0,0);
2563 addUpdateMeshTask(p, false, urgent);
2565 catch(InvalidPositionException &e){}
2567 if(nodepos.Y == blockpos_relative.Y){
2569 v3s16 p = blockpos + v3s16(0,-1,0);
2570 addUpdateMeshTask(p, false, urgent);
2572 catch(InvalidPositionException &e){}
2574 if(nodepos.Z == blockpos_relative.Z){
2576 v3s16 p = blockpos + v3s16(0,0,-1);
2577 addUpdateMeshTask(p, false, urgent);
2579 catch(InvalidPositionException &e){}
2583 ClientEvent Client::getClientEvent()
2585 if(m_client_event_queue.size() == 0)
2588 event.type = CE_NONE;
2591 return m_client_event_queue.pop_front();
2594 void Client::afterContentReceived()
2596 infostream<<"Client::afterContentReceived() started"<<std::endl;
2597 assert(m_itemdef_received);
2598 assert(m_nodedef_received);
2599 assert(texturesReceived());
2601 // remove the information about which checksum each texture
2603 m_media_name_sha1_map.clear();
2605 // Rebuild inherited images and recreate textures
2606 infostream<<"- Rebuilding images and textures"<<std::endl;
2607 m_tsrc->rebuildImagesAndTextures();
2609 // Update texture atlas
2610 infostream<<"- Updating texture atlas"<<std::endl;
2611 if(g_settings->getBool("enable_texture_atlas"))
2612 m_tsrc->buildMainAtlas(this);
2615 m_shsrc->rebuildShaders();
2617 // Update node aliases
2618 infostream<<"- Updating node aliases"<<std::endl;
2619 m_nodedef->updateAliases(m_itemdef);
2621 // Update node textures
2622 infostream<<"- Updating node textures"<<std::endl;
2623 m_nodedef->updateTextures(m_tsrc);
2625 // Preload item textures and meshes if configured to
2626 if(g_settings->getBool("preload_item_visuals"))
2628 verbosestream<<"Updating item textures and meshes"<<std::endl;
2629 std::set<std::string> names = m_itemdef->getAll();
2630 for(std::set<std::string>::const_iterator
2631 i = names.begin(); i != names.end(); ++i){
2632 // Asking for these caches the result
2633 m_itemdef->getInventoryTexture(*i, this);
2634 m_itemdef->getWieldMesh(*i, this);
2638 // Start mesh update thread after setting up content definitions
2639 infostream<<"- Starting mesh update thread"<<std::endl;
2640 m_mesh_update_thread.Start();
2642 infostream<<"Client::afterContentReceived() done"<<std::endl;
2645 float Client::getRTT(void)
2648 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2649 } catch(con::PeerNotFoundException &e){
2654 // IGameDef interface
2656 IItemDefManager* Client::getItemDefManager()
2660 INodeDefManager* Client::getNodeDefManager()
2664 ICraftDefManager* Client::getCraftDefManager()
2667 //return m_craftdef;
2669 ITextureSource* Client::getTextureSource()
2673 IShaderSource* Client::getShaderSource()
2677 u16 Client::allocateUnknownNodeId(const std::string &name)
2679 errorstream<<"Client::allocateUnknownNodeId(): "
2680 <<"Client cannot allocate node IDs"<<std::endl;
2682 return CONTENT_IGNORE;
2684 ISoundManager* Client::getSoundManager()
2688 MtEventManager* Client::getEventManager()