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);
1903 else if(command == TOCLIENT_SHOW_FORMSPEC)
1905 std::string datastring((char*)&data[2], datasize-2);
1906 std::istringstream is(datastring, std::ios_base::binary);
1908 std::string formspec = deSerializeLongString(is);
1909 std::string formname = deSerializeString(is);
1912 event.type = CE_SHOW_FORMSPEC;
1913 // pointer is required as event is a struct only!
1914 // adding a std:string to a struct isn't possible
1915 event.show_formspec.formspec = new std::string(formspec);
1916 event.show_formspec.formname = new std::string(formname);
1917 m_client_event_queue.push_back(event);
1921 infostream<<"Client: Ignoring unknown command "
1922 <<command<<std::endl;
1926 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1928 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1929 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1932 void Client::interact(u8 action, const PointedThing& pointed)
1934 if(connectedAndInitialized() == false){
1935 infostream<<"Client::interact() "
1936 "cancelled (not connected)"
1941 std::ostringstream os(std::ios_base::binary);
1947 [5] u32 length of the next item
1948 [9] serialized PointedThing
1950 0: start digging (from undersurface) or use
1951 1: stop digging (all parameters ignored)
1952 2: digging completed
1953 3: place block or item (to abovesurface)
1956 writeU16(os, TOSERVER_INTERACT);
1957 writeU8(os, action);
1958 writeU16(os, getPlayerItem());
1959 std::ostringstream tmp_os(std::ios::binary);
1960 pointed.serialize(tmp_os);
1961 os<<serializeLongString(tmp_os.str());
1963 std::string s = os.str();
1964 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1967 Send(0, data, true);
1970 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1971 const std::map<std::string, std::string> &fields)
1973 std::ostringstream os(std::ios_base::binary);
1975 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1977 os<<serializeString(formname);
1978 writeU16(os, fields.size());
1979 for(std::map<std::string, std::string>::const_iterator
1980 i = fields.begin(); i != fields.end(); i++){
1981 const std::string &name = i->first;
1982 const std::string &value = i->second;
1983 os<<serializeString(name);
1984 os<<serializeLongString(value);
1988 std::string s = os.str();
1989 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1991 Send(0, data, true);
1994 void Client::sendInventoryFields(const std::string &formname,
1995 const std::map<std::string, std::string> &fields)
1997 std::ostringstream os(std::ios_base::binary);
1999 writeU16(os, TOSERVER_INVENTORY_FIELDS);
2000 os<<serializeString(formname);
2001 writeU16(os, fields.size());
2002 for(std::map<std::string, std::string>::const_iterator
2003 i = fields.begin(); i != fields.end(); i++){
2004 const std::string &name = i->first;
2005 const std::string &value = i->second;
2006 os<<serializeString(name);
2007 os<<serializeLongString(value);
2011 std::string s = os.str();
2012 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2014 Send(0, data, true);
2017 void Client::sendInventoryAction(InventoryAction *a)
2019 std::ostringstream os(std::ios_base::binary);
2023 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2024 os.write((char*)buf, 2);
2029 std::string s = os.str();
2030 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2032 Send(0, data, true);
2035 void Client::sendChatMessage(const std::wstring &message)
2037 std::ostringstream os(std::ios_base::binary);
2041 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2042 os.write((char*)buf, 2);
2045 writeU16(buf, message.size());
2046 os.write((char*)buf, 2);
2049 for(u32 i=0; i<message.size(); i++)
2053 os.write((char*)buf, 2);
2057 std::string s = os.str();
2058 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2060 Send(0, data, true);
2063 void Client::sendChangePassword(const std::wstring oldpassword,
2064 const std::wstring newpassword)
2066 Player *player = m_env.getLocalPlayer();
2070 std::string playername = player->getName();
2071 std::string oldpwd = translatePassword(playername, oldpassword);
2072 std::string newpwd = translatePassword(playername, newpassword);
2074 std::ostringstream os(std::ios_base::binary);
2075 u8 buf[2+PASSWORD_SIZE*2];
2077 [0] u16 TOSERVER_PASSWORD
2078 [2] u8[28] old password
2079 [30] u8[28] new password
2082 writeU16(buf, TOSERVER_PASSWORD);
2083 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2085 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2086 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2088 buf[2+PASSWORD_SIZE-1] = 0;
2089 buf[30+PASSWORD_SIZE-1] = 0;
2090 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2093 std::string s = os.str();
2094 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2096 Send(0, data, true);
2100 void Client::sendDamage(u8 damage)
2102 DSTACK(__FUNCTION_NAME);
2103 std::ostringstream os(std::ios_base::binary);
2105 writeU16(os, TOSERVER_DAMAGE);
2106 writeU8(os, damage);
2109 std::string s = os.str();
2110 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2112 Send(0, data, true);
2115 void Client::sendRespawn()
2117 DSTACK(__FUNCTION_NAME);
2118 std::ostringstream os(std::ios_base::binary);
2120 writeU16(os, TOSERVER_RESPAWN);
2123 std::string s = os.str();
2124 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2126 Send(0, data, true);
2129 void Client::sendPlayerPos()
2131 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2133 LocalPlayer *myplayer = m_env.getLocalPlayer();
2134 if(myplayer == NULL)
2137 // Save bandwidth by only updating position when something changed
2138 if(myplayer->last_position == myplayer->getPosition() &&
2139 myplayer->last_speed == myplayer->getSpeed() &&
2140 myplayer->last_pitch == myplayer->getPitch() &&
2141 myplayer->last_yaw == myplayer->getYaw() &&
2142 myplayer->last_keyPressed == myplayer->keyPressed)
2145 myplayer->last_position = myplayer->getPosition();
2146 myplayer->last_speed = myplayer->getSpeed();
2147 myplayer->last_pitch = myplayer->getPitch();
2148 myplayer->last_yaw = myplayer->getYaw();
2149 myplayer->last_keyPressed = myplayer->keyPressed;
2153 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2154 our_peer_id = m_con.GetPeerID();
2157 // Set peer id if not set already
2158 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2159 myplayer->peer_id = our_peer_id;
2160 // Check that an existing peer_id is the same as the connection's
2161 assert(myplayer->peer_id == our_peer_id);
2163 v3f pf = myplayer->getPosition();
2164 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2165 v3f sf = myplayer->getSpeed();
2166 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2167 s32 pitch = myplayer->getPitch() * 100;
2168 s32 yaw = myplayer->getYaw() * 100;
2169 u32 keyPressed=myplayer->keyPressed;
2173 [2] v3s32 position*100
2174 [2+12] v3s32 speed*100
2175 [2+12+12] s32 pitch*100
2176 [2+12+12+4] s32 yaw*100
2177 [2+12+12+4+4] u32 keyPressed
2179 SharedBuffer<u8> data(2+12+12+4+4+4);
2180 writeU16(&data[0], TOSERVER_PLAYERPOS);
2181 writeV3S32(&data[2], position);
2182 writeV3S32(&data[2+12], speed);
2183 writeS32(&data[2+12+12], pitch);
2184 writeS32(&data[2+12+12+4], yaw);
2185 writeU32(&data[2+12+12+4+4], keyPressed);
2186 // Send as unreliable
2187 Send(0, data, false);
2190 void Client::sendPlayerItem(u16 item)
2192 Player *myplayer = m_env.getLocalPlayer();
2193 if(myplayer == NULL)
2196 u16 our_peer_id = m_con.GetPeerID();
2198 // Set peer id if not set already
2199 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2200 myplayer->peer_id = our_peer_id;
2201 // Check that an existing peer_id is the same as the connection's
2202 assert(myplayer->peer_id == our_peer_id);
2204 SharedBuffer<u8> data(2+2);
2205 writeU16(&data[0], TOSERVER_PLAYERITEM);
2206 writeU16(&data[2], item);
2209 Send(0, data, true);
2212 void Client::removeNode(v3s16 p)
2214 core::map<v3s16, MapBlock*> modified_blocks;
2218 //TimeTaker t("removeNodeAndUpdate", m_device);
2219 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2221 catch(InvalidPositionException &e)
2225 // add urgent task to update the modified node
2226 addUpdateMeshTaskForNode(p, false, true);
2228 for(core::map<v3s16, MapBlock * >::Iterator
2229 i = modified_blocks.getIterator();
2230 i.atEnd() == false; i++)
2232 v3s16 p = i.getNode()->getKey();
2233 addUpdateMeshTaskWithEdge(p);
2237 void Client::addNode(v3s16 p, MapNode n)
2239 TimeTaker timer1("Client::addNode()");
2241 core::map<v3s16, MapBlock*> modified_blocks;
2245 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2246 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2248 catch(InvalidPositionException &e)
2251 for(core::map<v3s16, MapBlock * >::Iterator
2252 i = modified_blocks.getIterator();
2253 i.atEnd() == false; i++)
2255 v3s16 p = i.getNode()->getKey();
2256 addUpdateMeshTaskWithEdge(p);
2260 void Client::setPlayerControl(PlayerControl &control)
2262 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2263 LocalPlayer *player = m_env.getLocalPlayer();
2264 assert(player != NULL);
2265 player->control = control;
2268 void Client::selectPlayerItem(u16 item)
2270 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2271 m_playeritem = item;
2272 m_inventory_updated = true;
2273 sendPlayerItem(item);
2276 // Returns true if the inventory of the local player has been
2277 // updated from the server. If it is true, it is set to false.
2278 bool Client::getLocalInventoryUpdated()
2280 // m_inventory_updated is behind envlock
2281 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2282 bool updated = m_inventory_updated;
2283 m_inventory_updated = false;
2287 // Copies the inventory of the local player to parameter
2288 void Client::getLocalInventory(Inventory &dst)
2290 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2291 Player *player = m_env.getLocalPlayer();
2292 assert(player != NULL);
2293 dst = player->inventory;
2296 Inventory* Client::getInventory(const InventoryLocation &loc)
2299 case InventoryLocation::UNDEFINED:
2302 case InventoryLocation::CURRENT_PLAYER:
2304 Player *player = m_env.getLocalPlayer();
2305 assert(player != NULL);
2306 return &player->inventory;
2309 case InventoryLocation::PLAYER:
2311 Player *player = m_env.getPlayer(loc.name.c_str());
2314 return &player->inventory;
2317 case InventoryLocation::NODEMETA:
2319 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2322 return meta->getInventory();
2325 case InventoryLocation::DETACHED:
2327 if(m_detached_inventories.count(loc.name) == 0)
2329 return m_detached_inventories[loc.name];
2337 void Client::inventoryAction(InventoryAction *a)
2340 Send it to the server
2342 sendInventoryAction(a);
2345 Predict some local inventory changes
2347 a->clientApply(this, this);
2350 ClientActiveObject * Client::getSelectedActiveObject(
2352 v3f from_pos_f_on_map,
2353 core::line3d<f32> shootline_on_map
2356 core::array<DistanceSortedActiveObject> objects;
2358 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2360 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2363 // After this, the closest object is the first in the array.
2366 for(u32 i=0; i<objects.size(); i++)
2368 ClientActiveObject *obj = objects[i].obj;
2370 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2371 if(selection_box == NULL)
2374 v3f pos = obj->getPosition();
2376 core::aabbox3d<f32> offsetted_box(
2377 selection_box->MinEdge + pos,
2378 selection_box->MaxEdge + pos
2381 if(offsetted_box.intersectsWithLine(shootline_on_map))
2383 //infostream<<"Returning selected object"<<std::endl;
2388 //infostream<<"No object selected; returning NULL."<<std::endl;
2392 void Client::printDebugInfo(std::ostream &os)
2394 //JMutexAutoLock lock1(m_fetchblock_mutex);
2395 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2397 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2398 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2399 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2403 core::list<std::wstring> Client::getConnectedPlayerNames()
2405 core::list<Player*> players = m_env.getPlayers(true);
2406 core::list<std::wstring> playerNames;
2407 for(core::list<Player*>::Iterator
2408 i = players.begin();
2409 i != players.end(); i++)
2411 Player *player = *i;
2412 playerNames.push_back(narrow_to_wide(player->getName()));
2417 float Client::getAnimationTime()
2419 return m_animation_time;
2422 int Client::getCrackLevel()
2424 return m_crack_level;
2427 void Client::setCrack(int level, v3s16 pos)
2429 int old_crack_level = m_crack_level;
2430 v3s16 old_crack_pos = m_crack_pos;
2432 m_crack_level = level;
2435 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2438 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2440 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2443 addUpdateMeshTaskForNode(pos, false, true);
2449 Player *player = m_env.getLocalPlayer();
2450 assert(player != NULL);
2454 bool Client::getChatMessage(std::wstring &message)
2456 if(m_chat_queue.size() == 0)
2458 message = m_chat_queue.pop_front();
2462 void Client::typeChatMessage(const std::wstring &message)
2464 // Discard empty line
2469 sendChatMessage(message);
2472 if (message[0] == L'/')
2474 m_chat_queue.push_back(
2475 (std::wstring)L"issued command: "+message);
2479 LocalPlayer *player = m_env.getLocalPlayer();
2480 assert(player != NULL);
2481 std::wstring name = narrow_to_wide(player->getName());
2482 m_chat_queue.push_back(
2483 (std::wstring)L"<"+name+L"> "+message);
2487 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2489 /*infostream<<"Client::addUpdateMeshTask(): "
2490 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2491 <<" ack_to_server="<<ack_to_server
2492 <<" urgent="<<urgent
2495 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2500 Create a task to update the mesh of the block
2503 MeshMakeData *data = new MeshMakeData(this);
2506 //TimeTaker timer("data fill");
2508 // Debug: 1-6ms, avg=2ms
2510 data->setCrack(m_crack_level, m_crack_pos);
2511 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2515 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2517 // Add task to queue
2518 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2520 /*infostream<<"Mesh update input queue size is "
2521 <<m_mesh_update_thread.m_queue_in.size()
2525 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2529 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2530 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2535 v3s16 p = blockpos + v3s16(0,0,0);
2536 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2537 addUpdateMeshTask(p, ack_to_server, urgent);
2539 catch(InvalidPositionException &e){}
2542 v3s16 p = blockpos + v3s16(-1,0,0);
2543 addUpdateMeshTask(p, false, urgent);
2545 catch(InvalidPositionException &e){}
2547 v3s16 p = blockpos + v3s16(0,-1,0);
2548 addUpdateMeshTask(p, false, urgent);
2550 catch(InvalidPositionException &e){}
2552 v3s16 p = blockpos + v3s16(0,0,-1);
2553 addUpdateMeshTask(p, false, urgent);
2555 catch(InvalidPositionException &e){}
2558 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2562 infostream<<"Client::addUpdateMeshTaskForNode(): "
2563 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2567 v3s16 blockpos = getNodeBlockPos(nodepos);
2568 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2571 v3s16 p = blockpos + v3s16(0,0,0);
2572 addUpdateMeshTask(p, ack_to_server, urgent);
2574 catch(InvalidPositionException &e){}
2576 if(nodepos.X == blockpos_relative.X){
2578 v3s16 p = blockpos + v3s16(-1,0,0);
2579 addUpdateMeshTask(p, false, urgent);
2581 catch(InvalidPositionException &e){}
2583 if(nodepos.Y == blockpos_relative.Y){
2585 v3s16 p = blockpos + v3s16(0,-1,0);
2586 addUpdateMeshTask(p, false, urgent);
2588 catch(InvalidPositionException &e){}
2590 if(nodepos.Z == blockpos_relative.Z){
2592 v3s16 p = blockpos + v3s16(0,0,-1);
2593 addUpdateMeshTask(p, false, urgent);
2595 catch(InvalidPositionException &e){}
2599 ClientEvent Client::getClientEvent()
2601 if(m_client_event_queue.size() == 0)
2604 event.type = CE_NONE;
2607 return m_client_event_queue.pop_front();
2610 void Client::afterContentReceived()
2612 infostream<<"Client::afterContentReceived() started"<<std::endl;
2613 assert(m_itemdef_received);
2614 assert(m_nodedef_received);
2615 assert(texturesReceived());
2617 // remove the information about which checksum each texture
2619 m_media_name_sha1_map.clear();
2621 // Rebuild inherited images and recreate textures
2622 infostream<<"- Rebuilding images and textures"<<std::endl;
2623 m_tsrc->rebuildImagesAndTextures();
2625 // Update texture atlas
2626 infostream<<"- Updating texture atlas"<<std::endl;
2627 if(g_settings->getBool("enable_texture_atlas"))
2628 m_tsrc->buildMainAtlas(this);
2631 m_shsrc->rebuildShaders();
2633 // Update node aliases
2634 infostream<<"- Updating node aliases"<<std::endl;
2635 m_nodedef->updateAliases(m_itemdef);
2637 // Update node textures
2638 infostream<<"- Updating node textures"<<std::endl;
2639 m_nodedef->updateTextures(m_tsrc);
2641 // Preload item textures and meshes if configured to
2642 if(g_settings->getBool("preload_item_visuals"))
2644 verbosestream<<"Updating item textures and meshes"<<std::endl;
2645 std::set<std::string> names = m_itemdef->getAll();
2646 for(std::set<std::string>::const_iterator
2647 i = names.begin(); i != names.end(); ++i){
2648 // Asking for these caches the result
2649 m_itemdef->getInventoryTexture(*i, this);
2650 m_itemdef->getWieldMesh(*i, this);
2654 // Start mesh update thread after setting up content definitions
2655 infostream<<"- Starting mesh update thread"<<std::endl;
2656 m_mesh_update_thread.Start();
2658 infostream<<"Client::afterContentReceived() done"<<std::endl;
2661 float Client::getRTT(void)
2664 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2665 } catch(con::PeerNotFoundException &e){
2670 // IGameDef interface
2672 IItemDefManager* Client::getItemDefManager()
2676 INodeDefManager* Client::getNodeDefManager()
2680 ICraftDefManager* Client::getCraftDefManager()
2683 //return m_craftdef;
2685 ITextureSource* Client::getTextureSource()
2689 IShaderSource* Client::getShaderSource()
2693 u16 Client::allocateUnknownNodeId(const std::string &name)
2695 errorstream<<"Client::allocateUnknownNodeId(): "
2696 <<"Client cannot allocate node IDs"<<std::endl;
2698 return CONTENT_IGNORE;
2700 ISoundManager* Client::getSoundManager()
2704 MtEventManager* Client::getEventManager()