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_URL, (m_remote_url + i->name).c_str());
240 curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
241 std::ostringstream stream;
242 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
243 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream);
244 res = curl_easy_perform(curl);
245 if (res == CURLE_OK) {
246 std::string data = stream.str();
247 m_file_data.push_back(make_pair(i->name, data));
249 m_failed.push_back(*i);
250 infostream << "cURL request failed for " << i->name << std::endl;
252 curl_easy_cleanup(curl);
256 END_DEBUG_EXCEPTION_HANDLER(errorstream)
262 IrrlichtDevice *device,
263 const char *playername,
264 std::string password,
265 MapDrawControl &control,
266 IWritableTextureSource *tsrc,
267 IWritableShaderSource *shsrc,
268 IWritableItemDefManager *itemdef,
269 IWritableNodeDefManager *nodedef,
270 ISoundManager *sound,
271 MtEventManager *event
279 m_mesh_update_thread(this),
281 new ClientMap(this, this, control,
282 device->getSceneManager()->getRootSceneNode(),
283 device->getSceneManager(), 666),
284 device->getSceneManager(),
287 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
289 m_server_ser_ver(SER_FMT_VER_INVALID),
291 m_inventory_updated(false),
292 m_inventory_from_server(NULL),
293 m_inventory_from_server_age(0.0),
298 m_password(password),
299 m_access_denied(false),
300 m_media_cache(getMediaCacheDir()),
301 m_media_receive_started(false),
303 m_media_received_count(0),
304 m_itemdef_received(false),
305 m_nodedef_received(false),
306 m_time_of_day_set(false),
307 m_last_time_of_day_f(-1),
308 m_time_of_day_update_timer(0),
309 m_recommended_send_interval(0.1),
310 m_removed_sounds_check_timer(0)
312 m_packetcounter_timer = 0.0;
313 //m_delete_unused_sectors_timer = 0.0;
314 m_connection_reinit_timer = 0.0;
315 m_avg_rtt_timer = 0.0;
316 m_playerpos_send_timer = 0.0;
317 m_ignore_damage_timer = 0.0;
319 // Build main texture atlas, now that the GameDef exists (that is, us)
320 if(g_settings->getBool("enable_texture_atlas"))
321 m_tsrc->buildMainAtlas(this);
323 infostream<<"Not building texture atlas."<<std::endl;
329 Player *player = new LocalPlayer(this);
331 player->updateName(playername);
333 m_env.addPlayer(player);
336 for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i)
337 m_media_fetch_threads.push_back(new MediaFetchThread(this));
343 //JMutexAutoLock conlock(m_con_mutex); //bulk comment-out
347 m_mesh_update_thread.setRun(false);
348 while(m_mesh_update_thread.IsRunning())
351 delete m_inventory_from_server;
353 // Delete detached inventories
355 for(std::map<std::string, Inventory*>::iterator
356 i = m_detached_inventories.begin();
357 i != m_detached_inventories.end(); i++){
362 for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
363 i != m_media_fetch_threads.end(); i++)
367 void Client::connect(Address address)
369 DSTACK(__FUNCTION_NAME);
370 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
371 m_con.SetTimeoutMs(0);
372 m_con.Connect(address);
375 bool Client::connectedAndInitialized()
377 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
379 if(m_con.Connected() == false)
382 if(m_server_ser_ver == SER_FMT_VER_INVALID)
388 void Client::step(float dtime)
390 DSTACK(__FUNCTION_NAME);
396 if(m_ignore_damage_timer > dtime)
397 m_ignore_damage_timer -= dtime;
399 m_ignore_damage_timer = 0.0;
401 m_animation_time += dtime;
402 if(m_animation_time > 60.0)
403 m_animation_time -= 60.0;
405 m_time_of_day_update_timer += dtime;
407 //infostream<<"Client steps "<<dtime<<std::endl;
410 //TimeTaker timer("ReceiveAll()", m_device);
416 //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device);
418 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
419 m_con.RunTimeouts(dtime);
426 float &counter = m_packetcounter_timer;
432 infostream<<"Client packetcounter (20s):"<<std::endl;
433 m_packetcounter.print(infostream);
434 m_packetcounter.clear();
438 // Get connection status
439 bool connected = connectedAndInitialized();
444 Delete unused sectors
446 NOTE: This jams the game for a while because deleting sectors
450 float &counter = m_delete_unused_sectors_timer;
458 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
460 core::list<v3s16> deleted_blocks;
462 float delete_unused_sectors_timeout =
463 g_settings->getFloat("client_delete_unused_sectors_timeout");
465 // Delete sector blocks
466 /*u32 num = m_env.getMap().unloadUnusedData
467 (delete_unused_sectors_timeout,
468 true, &deleted_blocks);*/
470 // Delete whole sectors
471 m_env.getMap().unloadUnusedData
472 (delete_unused_sectors_timeout,
475 if(deleted_blocks.size() > 0)
477 /*infostream<<"Client: Deleted blocks of "<<num
478 <<" unused sectors"<<std::endl;*/
479 /*infostream<<"Client: Deleted "<<num
480 <<" unused sectors"<<std::endl;*/
486 // Env is locked so con can be locked.
487 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
489 core::list<v3s16>::Iterator i = deleted_blocks.begin();
490 core::list<v3s16> sendlist;
493 if(sendlist.size() == 255 || i == deleted_blocks.end())
495 if(sendlist.size() == 0)
504 u32 replysize = 2+1+6*sendlist.size();
505 SharedBuffer<u8> reply(replysize);
506 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
507 reply[2] = sendlist.size();
509 for(core::list<v3s16>::Iterator
510 j = sendlist.begin();
511 j != sendlist.end(); j++)
513 writeV3S16(&reply[2+1+6*k], *j);
516 m_con.Send(PEER_ID_SERVER, 1, reply, true);
518 if(i == deleted_blocks.end())
524 sendlist.push_back(*i);
532 if(connected == false)
534 float &counter = m_connection_reinit_timer;
540 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
542 Player *myplayer = m_env.getLocalPlayer();
543 assert(myplayer != NULL);
545 // Send TOSERVER_INIT
546 // [0] u16 TOSERVER_INIT
547 // [2] u8 SER_FMT_VER_HIGHEST
548 // [3] u8[20] player_name
549 // [23] u8[28] password (new in some version)
550 // [51] u16 minimum supported network protocol version (added sometime)
551 // [53] u16 maximum supported network protocol version (added later than the previous one)
552 SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
553 writeU16(&data[0], TOSERVER_INIT);
554 writeU8(&data[2], SER_FMT_VER_HIGHEST);
556 memset((char*)&data[3], 0, PLAYERNAME_SIZE);
557 snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
559 /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
562 memset((char*)&data[23], 0, PASSWORD_SIZE);
563 snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
565 writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
566 writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
568 // Send as unreliable
569 Send(0, data, false);
572 // Not connected, return
577 Do stuff if connected
581 Run Map's timers and unload unused data
583 const float map_timer_and_unload_dtime = 5.25;
584 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
586 ScopeProfiler sp(g_profiler, "Client: map timer and unload");
587 core::list<v3s16> deleted_blocks;
588 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
589 g_settings->getFloat("client_unload_unused_data_timeout"),
592 /*if(deleted_blocks.size() > 0)
593 infostream<<"Client: Unloaded "<<deleted_blocks.size()
594 <<" unused blocks"<<std::endl;*/
598 NOTE: This loop is intentionally iterated the way it is.
601 core::list<v3s16>::Iterator i = deleted_blocks.begin();
602 core::list<v3s16> sendlist;
605 if(sendlist.size() == 255 || i == deleted_blocks.end())
607 if(sendlist.size() == 0)
616 u32 replysize = 2+1+6*sendlist.size();
617 SharedBuffer<u8> reply(replysize);
618 writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
619 reply[2] = sendlist.size();
621 for(core::list<v3s16>::Iterator
622 j = sendlist.begin();
623 j != sendlist.end(); j++)
625 writeV3S16(&reply[2+1+6*k], *j);
628 m_con.Send(PEER_ID_SERVER, 1, reply, true);
630 if(i == deleted_blocks.end())
636 sendlist.push_back(*i);
646 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
648 // Control local player (0ms)
649 LocalPlayer *player = m_env.getLocalPlayer();
650 assert(player != NULL);
651 player->applyControl(dtime);
653 //TimeTaker envtimer("env step", m_device);
662 ClientEnvEvent event = m_env.getClientEvent();
663 if(event.type == CEE_NONE)
667 else if(event.type == CEE_PLAYER_DAMAGE)
669 if(m_ignore_damage_timer <= 0)
671 u8 damage = event.player_damage.amount;
673 if(event.player_damage.send_to_server)
676 // Add to ClientEvent queue
678 event.type = CE_PLAYER_DAMAGE;
679 event.player_damage.amount = damage;
680 m_client_event_queue.push_back(event);
690 float &counter = m_avg_rtt_timer;
695 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
696 // connectedAndInitialized() is true, peer exists.
697 float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER);
698 infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
703 Send player position to server
706 float &counter = m_playerpos_send_timer;
708 if(counter >= m_recommended_send_interval)
716 Replace updated meshes
719 //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
721 //TimeTaker timer("** Processing mesh update result queue");
724 /*infostream<<"Mesh update result queue size is "
725 <<m_mesh_update_thread.m_queue_out.size()
728 int num_processed_meshes = 0;
729 while(m_mesh_update_thread.m_queue_out.size() > 0)
731 num_processed_meshes++;
732 MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front();
733 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
736 //JMutexAutoLock lock(block->mesh_mutex);
738 // Delete the old mesh
739 if(block->mesh != NULL)
741 // TODO: Remove hardware buffers of meshbuffers of block->mesh
746 // Replace with the new mesh
747 block->mesh = r.mesh;
749 if(r.ack_block_to_server)
751 /*infostream<<"Client: ACK block ("<<r.p.X<<","<<r.p.Y
752 <<","<<r.p.Z<<")"<<std::endl;*/
763 u32 replysize = 2+1+6;
764 SharedBuffer<u8> reply(replysize);
765 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
767 writeV3S16(&reply[3], r.p);
769 m_con.Send(PEER_ID_SERVER, 1, reply, true);
772 if(num_processed_meshes > 0)
773 g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
779 if (m_media_receive_started) {
780 bool all_stopped = true;
781 for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
782 thread != m_media_fetch_threads.end(); thread++) {
783 all_stopped &= !(*thread)->IsRunning();
784 while ((*thread)->m_file_data.size() > 0) {
785 std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
786 ++m_media_received_count;
788 bool success = loadMedia(out.second, out.first);
790 verbosestream<<"Client: Loaded received media: "
791 <<"\""<<out.first<<"\". Caching."<<std::endl;
793 infostream<<"Client: Failed to load received media: "
794 <<"\""<<out.first<<"\". Not caching."<<std::endl;
798 bool did = fs::CreateAllDirs(getMediaCacheDir());
800 errorstream<<"Could not create media cache directory"
805 core::map<std::string, std::string>::Node *n;
806 n = m_media_name_sha1_map.find(out.first);
808 errorstream<<"The server sent a file that has not "
809 <<"been announced."<<std::endl;
811 m_media_cache.update_sha1(out.second);
816 core::list<MediaRequest> fetch_failed;
817 for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin();
818 thread != m_media_fetch_threads.end(); thread++) {
819 for (core::list<MediaRequest>::Iterator request = (*thread)->m_failed.begin();
820 request != (*thread)->m_failed.end(); request++)
821 fetch_failed.push_back(*request);
822 (*thread)->m_failed.clear();
824 if (fetch_failed.size() > 0) {
825 infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
826 << "Requesting them the usual way." << std::endl;
827 request_media(fetch_failed);
833 If the server didn't update the inventory in a while, revert
834 the local inventory (so the player notices the lag problem
835 and knows something is wrong).
837 if(m_inventory_from_server)
839 float interval = 10.0;
840 float count_before = floor(m_inventory_from_server_age / interval);
842 m_inventory_from_server_age += dtime;
844 float count_after = floor(m_inventory_from_server_age / interval);
846 if(count_after != count_before)
848 // Do this every <interval> seconds after TOCLIENT_INVENTORY
849 // Reset the locally changed inventory to the authoritative inventory
850 Player *player = m_env.getLocalPlayer();
851 player->inventory = *m_inventory_from_server;
852 m_inventory_updated = true;
857 Update positions of sounds attached to objects
860 for(std::map<int, u16>::iterator
861 i = m_sounds_to_objects.begin();
862 i != m_sounds_to_objects.end(); i++)
864 int client_id = i->first;
865 u16 object_id = i->second;
866 ClientActiveObject *cao = m_env.getActiveObject(object_id);
869 v3f pos = cao->getPosition();
870 m_sound->updateSoundPosition(client_id, pos);
875 Handle removed remotely initiated sounds
877 m_removed_sounds_check_timer += dtime;
878 if(m_removed_sounds_check_timer >= 2.32)
880 m_removed_sounds_check_timer = 0;
881 // Find removed sounds and clear references to them
882 std::set<s32> removed_server_ids;
883 for(std::map<s32, int>::iterator
884 i = m_sounds_server_to_client.begin();
885 i != m_sounds_server_to_client.end();)
887 s32 server_id = i->first;
888 int client_id = i->second;
890 if(!m_sound->soundExists(client_id)){
891 m_sounds_server_to_client.erase(server_id);
892 m_sounds_client_to_server.erase(client_id);
893 m_sounds_to_objects.erase(client_id);
894 removed_server_ids.insert(server_id);
898 if(removed_server_ids.size() != 0)
900 std::ostringstream os(std::ios_base::binary);
901 writeU16(os, TOSERVER_REMOVED_SOUNDS);
902 writeU16(os, removed_server_ids.size());
903 for(std::set<s32>::iterator i = removed_server_ids.begin();
904 i != removed_server_ids.end(); i++)
906 std::string s = os.str();
907 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
914 bool Client::loadMedia(const std::string &data, const std::string &filename)
916 // Silly irrlicht's const-incorrectness
917 Buffer<char> data_rw(data.c_str(), data.size());
921 const char *image_ext[] = {
922 ".png", ".jpg", ".bmp", ".tga",
923 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
926 name = removeStringEnd(filename, image_ext);
929 verbosestream<<"Client: Attempting to load image "
930 <<"file \""<<filename<<"\""<<std::endl;
932 io::IFileSystem *irrfs = m_device->getFileSystem();
933 video::IVideoDriver *vdrv = m_device->getVideoDriver();
935 // Create an irrlicht memory file
936 io::IReadFile *rfile = irrfs->createMemoryReadFile(
937 *data_rw, data_rw.getSize(), "_tempreadfile");
940 video::IImage *img = vdrv->createImageFromFile(rfile);
942 errorstream<<"Client: Cannot create image from data of "
943 <<"file \""<<filename<<"\""<<std::endl;
948 m_tsrc->insertSourceImage(filename, img);
955 const char *sound_ext[] = {
956 ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
957 ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
960 name = removeStringEnd(filename, sound_ext);
963 verbosestream<<"Client: Attempting to load sound "
964 <<"file \""<<filename<<"\""<<std::endl;
965 m_sound->loadSoundData(name, data);
969 const char *model_ext[] = {
970 ".x", ".b3d", ".md2", ".obj",
973 name = removeStringEnd(filename, model_ext);
976 verbosestream<<"Client: Storing model into Irrlicht: "
977 <<"\""<<filename<<"\""<<std::endl;
979 io::IFileSystem *irrfs = m_device->getFileSystem();
980 io::IReadFile *rfile = irrfs->createMemoryReadFile(
981 *data_rw, data_rw.getSize(), filename.c_str());
984 scene::ISceneManager *smgr = m_device->getSceneManager();
985 scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
986 smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
991 errorstream<<"Client: Don't know how to load file \""
992 <<filename<<"\""<<std::endl;
996 // Virtual methods from con::PeerHandler
997 void Client::peerAdded(con::Peer *peer)
999 infostream<<"Client::peerAdded(): peer->id="
1000 <<peer->id<<std::endl;
1002 void Client::deletingPeer(con::Peer *peer, bool timeout)
1004 infostream<<"Client::deletingPeer(): "
1005 "Server Peer is getting deleted "
1006 <<"(timeout="<<timeout<<")"<<std::endl;
1011 u16 number of files requested
1017 void Client::request_media(const core::list<MediaRequest> &file_requests)
1019 std::ostringstream os(std::ios_base::binary);
1020 writeU16(os, TOSERVER_REQUEST_MEDIA);
1021 writeU16(os, file_requests.size());
1023 for(core::list<MediaRequest>::ConstIterator i = file_requests.begin();
1024 i != file_requests.end(); i++) {
1025 os<<serializeString(i->name);
1029 std::string s = os.str();
1030 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1032 Send(0, data, true);
1033 infostream<<"Client: Sending media request list to server ("
1034 <<file_requests.size()<<" files)"<<std::endl;
1037 void Client::ReceiveAll()
1039 DSTACK(__FUNCTION_NAME);
1040 u32 start_ms = porting::getTimeMs();
1043 // Limit time even if there would be huge amounts of data to
1045 if(porting::getTimeMs() > start_ms + 100)
1050 g_profiler->graphAdd("client_received_packets", 1);
1052 catch(con::NoIncomingDataException &e)
1056 catch(con::InvalidIncomingDataException &e)
1058 infostream<<"Client::ReceiveAll(): "
1059 "InvalidIncomingDataException: what()="
1060 <<e.what()<<std::endl;
1065 void Client::Receive()
1067 DSTACK(__FUNCTION_NAME);
1068 SharedBuffer<u8> data;
1072 //TimeTaker t1("con mutex and receive", m_device);
1073 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1074 datasize = m_con.Receive(sender_peer_id, data);
1076 //TimeTaker t1("ProcessData", m_device);
1077 ProcessData(*data, datasize, sender_peer_id);
1081 sender_peer_id given to this shall be quaranteed to be a valid peer
1083 void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
1085 DSTACK(__FUNCTION_NAME);
1087 // Ignore packets that don't even fit a command
1090 m_packetcounter.add(60000);
1094 ToClientCommand command = (ToClientCommand)readU16(&data[0]);
1096 //infostream<<"Client: received command="<<command<<std::endl;
1097 m_packetcounter.add((u16)command);
1100 If this check is removed, be sure to change the queue
1101 system to know the ids
1103 if(sender_peer_id != PEER_ID_SERVER)
1105 infostream<<"Client::ProcessData(): Discarding data not "
1106 "coming from server: peer_id="<<sender_peer_id
1111 u8 ser_version = m_server_ser_ver;
1113 //infostream<<"Client received command="<<(int)command<<std::endl;
1115 if(command == TOCLIENT_INIT)
1120 u8 deployed = data[2];
1122 infostream<<"Client: TOCLIENT_INIT received with "
1123 "deployed="<<((int)deployed&0xff)<<std::endl;
1125 if(deployed < SER_FMT_VER_LOWEST
1126 || deployed > SER_FMT_VER_HIGHEST)
1128 infostream<<"Client: TOCLIENT_INIT: Server sent "
1129 <<"unsupported ser_fmt_ver"<<std::endl;
1133 m_server_ser_ver = deployed;
1135 // Get player position
1136 v3s16 playerpos_s16(0, BS*2+BS*20, 0);
1137 if(datasize >= 2+1+6)
1138 playerpos_s16 = readV3S16(&data[2+1]);
1139 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
1142 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1144 // Set player position
1145 Player *player = m_env.getLocalPlayer();
1146 assert(player != NULL);
1147 player->setPosition(playerpos_f);
1150 if(datasize >= 2+1+6+8)
1153 m_map_seed = readU64(&data[2+1+6]);
1154 infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
1157 if(datasize >= 2+1+6+8+4)
1160 m_recommended_send_interval = readF1000(&data[2+1+6+8]);
1161 infostream<<"Client: received recommended send interval "
1162 <<m_recommended_send_interval<<std::endl;
1167 SharedBuffer<u8> reply(replysize);
1168 writeU16(&reply[0], TOSERVER_INIT2);
1170 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1175 if(command == TOCLIENT_ACCESS_DENIED)
1177 // The server didn't like our password. Note, this needs
1178 // to be processed even if the serialisation format has
1179 // not been agreed yet, the same as TOCLIENT_INIT.
1180 m_access_denied = true;
1181 m_access_denied_reason = L"Unknown";
1184 std::string datastring((char*)&data[2], datasize-2);
1185 std::istringstream is(datastring, std::ios_base::binary);
1186 m_access_denied_reason = deSerializeWideString(is);
1191 if(ser_version == SER_FMT_VER_INVALID)
1193 infostream<<"Client: Server serialization"
1194 " format invalid or not initialized."
1195 " Skipping incoming command="<<command<<std::endl;
1199 // Just here to avoid putting the two if's together when
1200 // making some copypasta
1203 if(command == TOCLIENT_REMOVENODE)
1208 p.X = readS16(&data[2]);
1209 p.Y = readS16(&data[4]);
1210 p.Z = readS16(&data[6]);
1212 //TimeTaker t1("TOCLIENT_REMOVENODE");
1216 else if(command == TOCLIENT_ADDNODE)
1218 if(datasize < 8 + MapNode::serializedLength(ser_version))
1222 p.X = readS16(&data[2]);
1223 p.Y = readS16(&data[4]);
1224 p.Z = readS16(&data[6]);
1226 //TimeTaker t1("TOCLIENT_ADDNODE");
1229 n.deSerialize(&data[8], ser_version);
1233 else if(command == TOCLIENT_BLOCKDATA)
1235 // Ignore too small packet
1240 p.X = readS16(&data[2]);
1241 p.Y = readS16(&data[4]);
1242 p.Z = readS16(&data[6]);
1244 /*infostream<<"Client: Thread: BLOCKDATA for ("
1245 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1246 /*infostream<<"Client: Thread: BLOCKDATA for ("
1247 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1249 std::string datastring((char*)&data[8], datasize-8);
1250 std::istringstream istr(datastring, std::ios_base::binary);
1255 v2s16 p2d(p.X, p.Z);
1256 sector = m_env.getMap().emergeSector(p2d);
1258 assert(sector->getPos() == p2d);
1260 //TimeTaker timer("MapBlock deSerialize");
1263 block = sector->getBlockNoCreateNoEx(p.Y);
1267 Update an existing block
1269 //infostream<<"Updating"<<std::endl;
1270 block->deSerialize(istr, ser_version, false);
1277 //infostream<<"Creating new"<<std::endl;
1278 block = new MapBlock(&m_env.getMap(), p, this);
1279 block->deSerialize(istr, ser_version, false);
1280 sector->insertBlock(block);
1294 u32 replysize = 2+1+6;
1295 SharedBuffer<u8> reply(replysize);
1296 writeU16(&reply[0], TOSERVER_GOTBLOCKS);
1298 writeV3S16(&reply[3], p);
1300 m_con.Send(PEER_ID_SERVER, 1, reply, true);
1304 Add it to mesh update queue and set it to be acknowledged after update.
1306 //infostream<<"Adding mesh update task for received block"<<std::endl;
1307 addUpdateMeshTaskWithEdge(p, true);
1309 else if(command == TOCLIENT_INVENTORY)
1314 //TimeTaker t1("Parsing TOCLIENT_INVENTORY", m_device);
1317 //TimeTaker t2("mutex locking", m_device);
1318 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1321 //TimeTaker t3("istringstream init", m_device);
1322 std::string datastring((char*)&data[2], datasize-2);
1323 std::istringstream is(datastring, std::ios_base::binary);
1326 //m_env.printPlayers(infostream);
1328 //TimeTaker t4("player get", m_device);
1329 Player *player = m_env.getLocalPlayer();
1330 assert(player != NULL);
1333 //TimeTaker t1("inventory.deSerialize()", m_device);
1334 player->inventory.deSerialize(is);
1337 m_inventory_updated = true;
1339 delete m_inventory_from_server;
1340 m_inventory_from_server = new Inventory(player->inventory);
1341 m_inventory_from_server_age = 0.0;
1343 //infostream<<"Client got player inventory:"<<std::endl;
1344 //player->inventory.print(infostream);
1347 else if(command == TOCLIENT_TIME_OF_DAY)
1352 u16 time_of_day = readU16(&data[2]);
1353 time_of_day = time_of_day % 24000;
1354 //infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
1355 float time_speed = 0;
1356 if(datasize >= 2 + 2 + 4){
1357 time_speed = readF1000(&data[4]);
1359 // Old message; try to approximate speed of time by ourselves
1360 float time_of_day_f = (float)time_of_day / 24000.0;
1361 float tod_diff_f = 0;
1362 if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
1363 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
1365 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
1366 m_last_time_of_day_f = time_of_day_f;
1367 float time_diff = m_time_of_day_update_timer;
1368 m_time_of_day_update_timer = 0;
1369 if(m_time_of_day_set){
1370 time_speed = 3600.0*24.0 * tod_diff_f / time_diff;
1371 infostream<<"Client: Measured time_of_day speed (old format): "
1372 <<time_speed<<" tod_diff_f="<<tod_diff_f
1373 <<" time_diff="<<time_diff<<std::endl;
1377 // Update environment
1378 m_env.setTimeOfDay(time_of_day);
1379 m_env.setTimeOfDaySpeed(time_speed);
1380 m_time_of_day_set = true;
1382 u32 dr = m_env.getDayNightRatio();
1383 verbosestream<<"Client: time_of_day="<<time_of_day
1384 <<" time_speed="<<time_speed
1385 <<" dr="<<dr<<std::endl;
1387 else if(command == TOCLIENT_CHAT_MESSAGE)
1395 std::string datastring((char*)&data[2], datasize-2);
1396 std::istringstream is(datastring, std::ios_base::binary);
1399 is.read((char*)buf, 2);
1400 u16 len = readU16(buf);
1402 std::wstring message;
1403 for(u16 i=0; i<len; i++)
1405 is.read((char*)buf, 2);
1406 message += (wchar_t)readU16(buf);
1409 /*infostream<<"Client received chat message: "
1410 <<wide_to_narrow(message)<<std::endl;*/
1412 m_chat_queue.push_back(message);
1414 else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
1416 //if(g_settings->getBool("enable_experimental"))
1420 u16 count of removed objects
1421 for all removed objects {
1424 u16 count of added objects
1425 for all added objects {
1428 u32 initialization data length
1429 string initialization data
1434 // Get all data except the command number
1435 std::string datastring((char*)&data[2], datasize-2);
1436 // Throw them in an istringstream
1437 std::istringstream is(datastring, std::ios_base::binary);
1441 // Read removed objects
1443 u16 removed_count = readU16((u8*)buf);
1444 for(u16 i=0; i<removed_count; i++)
1447 u16 id = readU16((u8*)buf);
1450 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1451 m_env.removeActiveObject(id);
1455 // Read added objects
1457 u16 added_count = readU16((u8*)buf);
1458 for(u16 i=0; i<added_count; i++)
1461 u16 id = readU16((u8*)buf);
1463 u8 type = readU8((u8*)buf);
1464 std::string data = deSerializeLongString(is);
1467 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1468 m_env.addActiveObject(id, type, data);
1473 else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
1475 //if(g_settings->getBool("enable_experimental"))
1487 // Get all data except the command number
1488 std::string datastring((char*)&data[2], datasize-2);
1489 // Throw them in an istringstream
1490 std::istringstream is(datastring, std::ios_base::binary);
1492 while(is.eof() == false)
1496 u16 id = readU16((u8*)buf);
1500 u16 message_size = readU16((u8*)buf);
1501 std::string message;
1502 message.reserve(message_size);
1503 for(u16 i=0; i<message_size; i++)
1506 message.append(buf, 1);
1508 // Pass on to the environment
1510 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
1511 m_env.processActiveObjectMessage(id, message);
1516 else if(command == TOCLIENT_HP)
1518 std::string datastring((char*)&data[2], datasize-2);
1519 std::istringstream is(datastring, std::ios_base::binary);
1520 Player *player = m_env.getLocalPlayer();
1521 assert(player != NULL);
1522 u8 oldhp = player->hp;
1528 // Add to ClientEvent queue
1530 event.type = CE_PLAYER_DAMAGE;
1531 event.player_damage.amount = oldhp - hp;
1532 m_client_event_queue.push_back(event);
1535 else if(command == TOCLIENT_MOVE_PLAYER)
1537 std::string datastring((char*)&data[2], datasize-2);
1538 std::istringstream is(datastring, std::ios_base::binary);
1539 Player *player = m_env.getLocalPlayer();
1540 assert(player != NULL);
1541 v3f pos = readV3F1000(is);
1542 f32 pitch = readF1000(is);
1543 f32 yaw = readF1000(is);
1544 player->setPosition(pos);
1545 /*player->setPitch(pitch);
1546 player->setYaw(yaw);*/
1548 infostream<<"Client got TOCLIENT_MOVE_PLAYER"
1549 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
1555 Add to ClientEvent queue.
1556 This has to be sent to the main program because otherwise
1557 it would just force the pitch and yaw values to whatever
1558 the camera points to.
1561 event.type = CE_PLAYER_FORCE_MOVE;
1562 event.player_force_move.pitch = pitch;
1563 event.player_force_move.yaw = yaw;
1564 m_client_event_queue.push_back(event);
1566 // Ignore damage for a few seconds, so that the player doesn't
1567 // get damage from falling on ground
1568 m_ignore_damage_timer = 3.0;
1570 else if(command == TOCLIENT_PLAYERITEM)
1572 infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
1574 else if(command == TOCLIENT_DEATHSCREEN)
1576 std::string datastring((char*)&data[2], datasize-2);
1577 std::istringstream is(datastring, std::ios_base::binary);
1579 bool set_camera_point_target = readU8(is);
1580 v3f camera_point_target = readV3F1000(is);
1583 event.type = CE_DEATHSCREEN;
1584 event.deathscreen.set_camera_point_target = set_camera_point_target;
1585 event.deathscreen.camera_point_target_x = camera_point_target.X;
1586 event.deathscreen.camera_point_target_y = camera_point_target.Y;
1587 event.deathscreen.camera_point_target_z = camera_point_target.Z;
1588 m_client_event_queue.push_back(event);
1590 else if(command == TOCLIENT_ANNOUNCE_MEDIA)
1592 std::string datastring((char*)&data[2], datasize-2);
1593 std::istringstream is(datastring, std::ios_base::binary);
1595 // Mesh update thread must be stopped while
1596 // updating content definitions
1597 assert(!m_mesh_update_thread.IsRunning());
1599 int num_files = readU16(is);
1601 infostream<<"Client: Received media announcement: packet size: "
1602 <<datasize<<std::endl;
1604 core::list<MediaRequest> file_requests;
1606 for(int i=0; i<num_files; i++)
1608 //read file from cache
1609 std::string name = deSerializeString(is);
1610 std::string sha1_base64 = deSerializeString(is);
1612 // if name contains illegal characters, ignore the file
1613 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1614 errorstream<<"Client: ignoring illegal file name "
1615 <<"sent by server: \""<<name<<"\""<<std::endl;
1619 std::string sha1_raw = base64_decode(sha1_base64);
1620 std::string sha1_hex = hex_encode(sha1_raw);
1621 std::ostringstream tmp_os(std::ios_base::binary);
1622 bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os);
1623 m_media_name_sha1_map.set(name, sha1_raw);
1625 // If found in cache, try to load it from there
1628 bool success = loadMedia(tmp_os.str(), name);
1630 verbosestream<<"Client: Loaded cached media: "
1631 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1634 infostream<<"Client: Failed to load cached media: "
1635 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1638 // Didn't load from cache; queue it to be requested
1639 verbosestream<<"Client: Adding file to request list: \""
1640 <<sha1_hex<<" \""<<name<<"\""<<std::endl;
1641 file_requests.push_back(MediaRequest(name));
1644 std::string remote_media = "";
1646 remote_media = deSerializeString(is);
1648 catch(SerializationError) {
1649 // not supported by server or turned off
1652 m_media_count = file_requests.size();
1653 m_media_receive_started = true;
1655 if (remote_media == "" || !USE_CURL) {
1656 request_media(file_requests);
1659 core::list<MediaFetchThread*>::Iterator cur = m_media_fetch_threads.begin();
1660 for(core::list<MediaRequest>::Iterator i = file_requests.begin();
1661 i != file_requests.end(); i++) {
1662 (*cur)->m_file_requests.push_back(*i);
1664 if (cur == m_media_fetch_threads.end())
1665 cur = m_media_fetch_threads.begin();
1667 for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin();
1668 i != m_media_fetch_threads.end(); i++) {
1669 (*i)->m_remote_url = remote_media;
1674 // notify server we received everything
1675 std::ostringstream os(std::ios_base::binary);
1676 writeU16(os, TOSERVER_RECEIVED_MEDIA);
1677 std::string s = os.str();
1678 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1680 Send(0, data, true);
1683 event.type = CE_TEXTURES_UPDATED;
1684 m_client_event_queue.push_back(event);
1686 else if(command == TOCLIENT_MEDIA)
1688 if (m_media_count == 0)
1690 std::string datastring((char*)&data[2], datasize-2);
1691 std::istringstream is(datastring, std::ios_base::binary);
1693 // Mesh update thread must be stopped while
1694 // updating content definitions
1695 assert(!m_mesh_update_thread.IsRunning());
1699 u16 total number of file bunches
1700 u16 index of this bunch
1701 u32 number of files in this bunch
1709 int num_bunches = readU16(is);
1710 int bunch_i = readU16(is);
1711 int num_files = readU32(is);
1712 infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
1713 <<num_bunches<<" files="<<num_files
1714 <<" size="<<datasize<<std::endl;
1715 for(int i=0; i<num_files; i++){
1716 m_media_received_count++;
1717 std::string name = deSerializeString(is);
1718 std::string data = deSerializeLongString(is);
1720 // if name contains illegal characters, ignore the file
1721 if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
1722 errorstream<<"Client: ignoring illegal file name "
1723 <<"sent by server: \""<<name<<"\""<<std::endl;
1727 bool success = loadMedia(data, name);
1729 verbosestream<<"Client: Loaded received media: "
1730 <<"\""<<name<<"\". Caching."<<std::endl;
1732 infostream<<"Client: Failed to load received media: "
1733 <<"\""<<name<<"\". Not caching."<<std::endl;
1737 bool did = fs::CreateAllDirs(getMediaCacheDir());
1739 errorstream<<"Could not create media cache directory"
1744 core::map<std::string, std::string>::Node *n;
1745 n = m_media_name_sha1_map.find(name);
1747 errorstream<<"The server sent a file that has not "
1748 <<"been announced."<<std::endl;
1750 m_media_cache.update_sha1(data);
1755 event.type = CE_TEXTURES_UPDATED;
1756 m_client_event_queue.push_back(event);
1758 else if(command == TOCLIENT_TOOLDEF)
1760 infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
1762 else if(command == TOCLIENT_NODEDEF)
1764 infostream<<"Client: Received node definitions: packet size: "
1765 <<datasize<<std::endl;
1767 // Mesh update thread must be stopped while
1768 // updating content definitions
1769 assert(!m_mesh_update_thread.IsRunning());
1771 // Decompress node definitions
1772 std::string datastring((char*)&data[2], datasize-2);
1773 std::istringstream is(datastring, std::ios_base::binary);
1774 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1775 std::ostringstream tmp_os;
1776 decompressZlib(tmp_is, tmp_os);
1778 // Deserialize node definitions
1779 std::istringstream tmp_is2(tmp_os.str());
1780 m_nodedef->deSerialize(tmp_is2);
1781 m_nodedef_received = true;
1783 else if(command == TOCLIENT_CRAFTITEMDEF)
1785 infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
1787 else if(command == TOCLIENT_ITEMDEF)
1789 infostream<<"Client: Received item definitions: packet size: "
1790 <<datasize<<std::endl;
1792 // Mesh update thread must be stopped while
1793 // updating content definitions
1794 assert(!m_mesh_update_thread.IsRunning());
1796 // Decompress item definitions
1797 std::string datastring((char*)&data[2], datasize-2);
1798 std::istringstream is(datastring, std::ios_base::binary);
1799 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
1800 std::ostringstream tmp_os;
1801 decompressZlib(tmp_is, tmp_os);
1803 // Deserialize node definitions
1804 std::istringstream tmp_is2(tmp_os.str());
1805 m_itemdef->deSerialize(tmp_is2);
1806 m_itemdef_received = true;
1808 else if(command == TOCLIENT_PLAY_SOUND)
1810 std::string datastring((char*)&data[2], datasize-2);
1811 std::istringstream is(datastring, std::ios_base::binary);
1813 s32 server_id = readS32(is);
1814 std::string name = deSerializeString(is);
1815 float gain = readF1000(is);
1816 int type = readU8(is); // 0=local, 1=positional, 2=object
1817 v3f pos = readV3F1000(is);
1818 u16 object_id = readU16(is);
1819 bool loop = readU8(is);
1824 client_id = m_sound->playSound(name, loop, gain);
1826 case 1: // positional
1827 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1830 ClientActiveObject *cao = m_env.getActiveObject(object_id);
1832 pos = cao->getPosition();
1833 client_id = m_sound->playSoundAt(name, loop, gain, pos);
1834 // TODO: Set up sound to move with object
1839 if(client_id != -1){
1840 m_sounds_server_to_client[server_id] = client_id;
1841 m_sounds_client_to_server[client_id] = server_id;
1843 m_sounds_to_objects[client_id] = object_id;
1846 else if(command == TOCLIENT_STOP_SOUND)
1848 std::string datastring((char*)&data[2], datasize-2);
1849 std::istringstream is(datastring, std::ios_base::binary);
1851 s32 server_id = readS32(is);
1852 std::map<s32, int>::iterator i =
1853 m_sounds_server_to_client.find(server_id);
1854 if(i != m_sounds_server_to_client.end()){
1855 int client_id = i->second;
1856 m_sound->stopSound(client_id);
1859 else if(command == TOCLIENT_PRIVILEGES)
1861 std::string datastring((char*)&data[2], datasize-2);
1862 std::istringstream is(datastring, std::ios_base::binary);
1864 m_privileges.clear();
1865 infostream<<"Client: Privileges updated: ";
1866 u16 num_privileges = readU16(is);
1867 for(u16 i=0; i<num_privileges; i++){
1868 std::string priv = deSerializeString(is);
1869 m_privileges.insert(priv);
1870 infostream<<priv<<" ";
1872 infostream<<std::endl;
1874 else if(command == TOCLIENT_INVENTORY_FORMSPEC)
1876 std::string datastring((char*)&data[2], datasize-2);
1877 std::istringstream is(datastring, std::ios_base::binary);
1879 // Store formspec in LocalPlayer
1880 Player *player = m_env.getLocalPlayer();
1881 assert(player != NULL);
1882 player->inventory_formspec = deSerializeLongString(is);
1884 else if(command == TOCLIENT_DETACHED_INVENTORY)
1886 std::string datastring((char*)&data[2], datasize-2);
1887 std::istringstream is(datastring, std::ios_base::binary);
1889 std::string name = deSerializeString(is);
1891 infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
1893 Inventory *inv = NULL;
1894 if(m_detached_inventories.count(name) > 0)
1895 inv = m_detached_inventories[name];
1897 inv = new Inventory(m_itemdef);
1898 m_detached_inventories[name] = inv;
1900 inv->deSerialize(is);
1904 infostream<<"Client: Ignoring unknown command "
1905 <<command<<std::endl;
1909 void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
1911 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
1912 m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
1915 void Client::interact(u8 action, const PointedThing& pointed)
1917 if(connectedAndInitialized() == false){
1918 infostream<<"Client::interact() "
1919 "cancelled (not connected)"
1924 std::ostringstream os(std::ios_base::binary);
1930 [5] u32 length of the next item
1931 [9] serialized PointedThing
1933 0: start digging (from undersurface) or use
1934 1: stop digging (all parameters ignored)
1935 2: digging completed
1936 3: place block or item (to abovesurface)
1939 writeU16(os, TOSERVER_INTERACT);
1940 writeU8(os, action);
1941 writeU16(os, getPlayerItem());
1942 std::ostringstream tmp_os(std::ios::binary);
1943 pointed.serialize(tmp_os);
1944 os<<serializeLongString(tmp_os.str());
1946 std::string s = os.str();
1947 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1950 Send(0, data, true);
1953 void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
1954 const std::map<std::string, std::string> &fields)
1956 std::ostringstream os(std::ios_base::binary);
1958 writeU16(os, TOSERVER_NODEMETA_FIELDS);
1960 os<<serializeString(formname);
1961 writeU16(os, fields.size());
1962 for(std::map<std::string, std::string>::const_iterator
1963 i = fields.begin(); i != fields.end(); i++){
1964 const std::string &name = i->first;
1965 const std::string &value = i->second;
1966 os<<serializeString(name);
1967 os<<serializeLongString(value);
1971 std::string s = os.str();
1972 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1974 Send(0, data, true);
1977 void Client::sendInventoryFields(const std::string &formname,
1978 const std::map<std::string, std::string> &fields)
1980 std::ostringstream os(std::ios_base::binary);
1982 writeU16(os, TOSERVER_INVENTORY_FIELDS);
1983 os<<serializeString(formname);
1984 writeU16(os, fields.size());
1985 for(std::map<std::string, std::string>::const_iterator
1986 i = fields.begin(); i != fields.end(); i++){
1987 const std::string &name = i->first;
1988 const std::string &value = i->second;
1989 os<<serializeString(name);
1990 os<<serializeLongString(value);
1994 std::string s = os.str();
1995 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
1997 Send(0, data, true);
2000 void Client::sendInventoryAction(InventoryAction *a)
2002 std::ostringstream os(std::ios_base::binary);
2006 writeU16(buf, TOSERVER_INVENTORY_ACTION);
2007 os.write((char*)buf, 2);
2012 std::string s = os.str();
2013 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2015 Send(0, data, true);
2018 void Client::sendChatMessage(const std::wstring &message)
2020 std::ostringstream os(std::ios_base::binary);
2024 writeU16(buf, TOSERVER_CHAT_MESSAGE);
2025 os.write((char*)buf, 2);
2028 writeU16(buf, message.size());
2029 os.write((char*)buf, 2);
2032 for(u32 i=0; i<message.size(); i++)
2036 os.write((char*)buf, 2);
2040 std::string s = os.str();
2041 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2043 Send(0, data, true);
2046 void Client::sendChangePassword(const std::wstring oldpassword,
2047 const std::wstring newpassword)
2049 Player *player = m_env.getLocalPlayer();
2053 std::string playername = player->getName();
2054 std::string oldpwd = translatePassword(playername, oldpassword);
2055 std::string newpwd = translatePassword(playername, newpassword);
2057 std::ostringstream os(std::ios_base::binary);
2058 u8 buf[2+PASSWORD_SIZE*2];
2060 [0] u16 TOSERVER_PASSWORD
2061 [2] u8[28] old password
2062 [30] u8[28] new password
2065 writeU16(buf, TOSERVER_PASSWORD);
2066 for(u32 i=0;i<PASSWORD_SIZE-1;i++)
2068 buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
2069 buf[30+i] = i<newpwd.length()?newpwd[i]:0;
2071 buf[2+PASSWORD_SIZE-1] = 0;
2072 buf[30+PASSWORD_SIZE-1] = 0;
2073 os.write((char*)buf, 2+PASSWORD_SIZE*2);
2076 std::string s = os.str();
2077 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2079 Send(0, data, true);
2083 void Client::sendDamage(u8 damage)
2085 DSTACK(__FUNCTION_NAME);
2086 std::ostringstream os(std::ios_base::binary);
2088 writeU16(os, TOSERVER_DAMAGE);
2089 writeU8(os, damage);
2092 std::string s = os.str();
2093 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2095 Send(0, data, true);
2098 void Client::sendRespawn()
2100 DSTACK(__FUNCTION_NAME);
2101 std::ostringstream os(std::ios_base::binary);
2103 writeU16(os, TOSERVER_RESPAWN);
2106 std::string s = os.str();
2107 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2109 Send(0, data, true);
2112 void Client::sendPlayerPos()
2114 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2116 LocalPlayer *myplayer = m_env.getLocalPlayer();
2117 if(myplayer == NULL)
2120 // Save bandwidth by only updating position when something changed
2121 if(myplayer->last_position == myplayer->getPosition() &&
2122 myplayer->last_speed == myplayer->getSpeed() &&
2123 myplayer->last_pitch == myplayer->getPitch() &&
2124 myplayer->last_yaw == myplayer->getYaw() &&
2125 myplayer->last_keyPressed == myplayer->keyPressed)
2128 myplayer->last_position = myplayer->getPosition();
2129 myplayer->last_speed = myplayer->getSpeed();
2130 myplayer->last_pitch = myplayer->getPitch();
2131 myplayer->last_yaw = myplayer->getYaw();
2132 myplayer->last_keyPressed = myplayer->keyPressed;
2136 //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
2137 our_peer_id = m_con.GetPeerID();
2140 // Set peer id if not set already
2141 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2142 myplayer->peer_id = our_peer_id;
2143 // Check that an existing peer_id is the same as the connection's
2144 assert(myplayer->peer_id == our_peer_id);
2146 v3f pf = myplayer->getPosition();
2147 v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
2148 v3f sf = myplayer->getSpeed();
2149 v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
2150 s32 pitch = myplayer->getPitch() * 100;
2151 s32 yaw = myplayer->getYaw() * 100;
2152 u32 keyPressed=myplayer->keyPressed;
2156 [2] v3s32 position*100
2157 [2+12] v3s32 speed*100
2158 [2+12+12] s32 pitch*100
2159 [2+12+12+4] s32 yaw*100
2160 [2+12+12+4+4] u32 keyPressed
2162 SharedBuffer<u8> data(2+12+12+4+4+4);
2163 writeU16(&data[0], TOSERVER_PLAYERPOS);
2164 writeV3S32(&data[2], position);
2165 writeV3S32(&data[2+12], speed);
2166 writeS32(&data[2+12+12], pitch);
2167 writeS32(&data[2+12+12+4], yaw);
2168 writeU32(&data[2+12+12+4+4], keyPressed);
2169 // Send as unreliable
2170 Send(0, data, false);
2173 void Client::sendPlayerItem(u16 item)
2175 Player *myplayer = m_env.getLocalPlayer();
2176 if(myplayer == NULL)
2179 u16 our_peer_id = m_con.GetPeerID();
2181 // Set peer id if not set already
2182 if(myplayer->peer_id == PEER_ID_INEXISTENT)
2183 myplayer->peer_id = our_peer_id;
2184 // Check that an existing peer_id is the same as the connection's
2185 assert(myplayer->peer_id == our_peer_id);
2187 SharedBuffer<u8> data(2+2);
2188 writeU16(&data[0], TOSERVER_PLAYERITEM);
2189 writeU16(&data[2], item);
2192 Send(0, data, true);
2195 void Client::removeNode(v3s16 p)
2197 core::map<v3s16, MapBlock*> modified_blocks;
2201 //TimeTaker t("removeNodeAndUpdate", m_device);
2202 m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
2204 catch(InvalidPositionException &e)
2208 // add urgent task to update the modified node
2209 addUpdateMeshTaskForNode(p, false, true);
2211 for(core::map<v3s16, MapBlock * >::Iterator
2212 i = modified_blocks.getIterator();
2213 i.atEnd() == false; i++)
2215 v3s16 p = i.getNode()->getKey();
2216 addUpdateMeshTaskWithEdge(p);
2220 void Client::addNode(v3s16 p, MapNode n)
2222 TimeTaker timer1("Client::addNode()");
2224 core::map<v3s16, MapBlock*> modified_blocks;
2228 //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
2229 m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
2231 catch(InvalidPositionException &e)
2234 for(core::map<v3s16, MapBlock * >::Iterator
2235 i = modified_blocks.getIterator();
2236 i.atEnd() == false; i++)
2238 v3s16 p = i.getNode()->getKey();
2239 addUpdateMeshTaskWithEdge(p);
2243 void Client::setPlayerControl(PlayerControl &control)
2245 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2246 LocalPlayer *player = m_env.getLocalPlayer();
2247 assert(player != NULL);
2248 player->control = control;
2251 void Client::selectPlayerItem(u16 item)
2253 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2254 m_playeritem = item;
2255 m_inventory_updated = true;
2256 sendPlayerItem(item);
2259 // Returns true if the inventory of the local player has been
2260 // updated from the server. If it is true, it is set to false.
2261 bool Client::getLocalInventoryUpdated()
2263 // m_inventory_updated is behind envlock
2264 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2265 bool updated = m_inventory_updated;
2266 m_inventory_updated = false;
2270 // Copies the inventory of the local player to parameter
2271 void Client::getLocalInventory(Inventory &dst)
2273 //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
2274 Player *player = m_env.getLocalPlayer();
2275 assert(player != NULL);
2276 dst = player->inventory;
2279 Inventory* Client::getInventory(const InventoryLocation &loc)
2282 case InventoryLocation::UNDEFINED:
2285 case InventoryLocation::CURRENT_PLAYER:
2287 Player *player = m_env.getLocalPlayer();
2288 assert(player != NULL);
2289 return &player->inventory;
2292 case InventoryLocation::PLAYER:
2294 Player *player = m_env.getPlayer(loc.name.c_str());
2297 return &player->inventory;
2300 case InventoryLocation::NODEMETA:
2302 NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
2305 return meta->getInventory();
2308 case InventoryLocation::DETACHED:
2310 if(m_detached_inventories.count(loc.name) == 0)
2312 return m_detached_inventories[loc.name];
2320 void Client::inventoryAction(InventoryAction *a)
2323 Send it to the server
2325 sendInventoryAction(a);
2328 Predict some local inventory changes
2330 a->clientApply(this, this);
2333 ClientActiveObject * Client::getSelectedActiveObject(
2335 v3f from_pos_f_on_map,
2336 core::line3d<f32> shootline_on_map
2339 core::array<DistanceSortedActiveObject> objects;
2341 m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
2343 //infostream<<"Collected "<<objects.size()<<" nearby objects"<<std::endl;
2346 // After this, the closest object is the first in the array.
2349 for(u32 i=0; i<objects.size(); i++)
2351 ClientActiveObject *obj = objects[i].obj;
2353 core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
2354 if(selection_box == NULL)
2357 v3f pos = obj->getPosition();
2359 core::aabbox3d<f32> offsetted_box(
2360 selection_box->MinEdge + pos,
2361 selection_box->MaxEdge + pos
2364 if(offsetted_box.intersectsWithLine(shootline_on_map))
2366 //infostream<<"Returning selected object"<<std::endl;
2371 //infostream<<"No object selected; returning NULL."<<std::endl;
2375 void Client::printDebugInfo(std::ostream &os)
2377 //JMutexAutoLock lock1(m_fetchblock_mutex);
2378 /*JMutexAutoLock lock2(m_incoming_queue_mutex);
2380 os<<"m_incoming_queue.getSize()="<<m_incoming_queue.getSize()
2381 //<<", m_fetchblock_history.size()="<<m_fetchblock_history.size()
2382 //<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
2386 core::list<std::wstring> Client::getConnectedPlayerNames()
2388 core::list<Player*> players = m_env.getPlayers(true);
2389 core::list<std::wstring> playerNames;
2390 for(core::list<Player*>::Iterator
2391 i = players.begin();
2392 i != players.end(); i++)
2394 Player *player = *i;
2395 playerNames.push_back(narrow_to_wide(player->getName()));
2400 float Client::getAnimationTime()
2402 return m_animation_time;
2405 int Client::getCrackLevel()
2407 return m_crack_level;
2410 void Client::setCrack(int level, v3s16 pos)
2412 int old_crack_level = m_crack_level;
2413 v3s16 old_crack_pos = m_crack_pos;
2415 m_crack_level = level;
2418 if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
2421 addUpdateMeshTaskForNode(old_crack_pos, false, true);
2423 if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
2426 addUpdateMeshTaskForNode(pos, false, true);
2432 Player *player = m_env.getLocalPlayer();
2433 assert(player != NULL);
2437 bool Client::getChatMessage(std::wstring &message)
2439 if(m_chat_queue.size() == 0)
2441 message = m_chat_queue.pop_front();
2445 void Client::typeChatMessage(const std::wstring &message)
2447 // Discard empty line
2452 sendChatMessage(message);
2455 if (message[0] == L'/')
2457 m_chat_queue.push_back(
2458 (std::wstring)L"issued command: "+message);
2462 LocalPlayer *player = m_env.getLocalPlayer();
2463 assert(player != NULL);
2464 std::wstring name = narrow_to_wide(player->getName());
2465 m_chat_queue.push_back(
2466 (std::wstring)L"<"+name+L"> "+message);
2470 void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
2472 /*infostream<<"Client::addUpdateMeshTask(): "
2473 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2474 <<" ack_to_server="<<ack_to_server
2475 <<" urgent="<<urgent
2478 MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
2483 Create a task to update the mesh of the block
2486 MeshMakeData *data = new MeshMakeData(this);
2489 //TimeTaker timer("data fill");
2491 // Debug: 1-6ms, avg=2ms
2493 data->setCrack(m_crack_level, m_crack_pos);
2494 data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
2498 //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
2500 // Add task to queue
2501 m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
2503 /*infostream<<"Mesh update input queue size is "
2504 <<m_mesh_update_thread.m_queue_in.size()
2508 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
2512 infostream<<"Client::addUpdateMeshTaskWithEdge(): "
2513 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2518 v3s16 p = blockpos + v3s16(0,0,0);
2519 //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
2520 addUpdateMeshTask(p, ack_to_server, urgent);
2522 catch(InvalidPositionException &e){}
2525 v3s16 p = blockpos + v3s16(-1,0,0);
2526 addUpdateMeshTask(p, false, urgent);
2528 catch(InvalidPositionException &e){}
2530 v3s16 p = blockpos + v3s16(0,-1,0);
2531 addUpdateMeshTask(p, false, urgent);
2533 catch(InvalidPositionException &e){}
2535 v3s16 p = blockpos + v3s16(0,0,-1);
2536 addUpdateMeshTask(p, false, urgent);
2538 catch(InvalidPositionException &e){}
2541 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
2545 infostream<<"Client::addUpdateMeshTaskForNode(): "
2546 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2550 v3s16 blockpos = getNodeBlockPos(nodepos);
2551 v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
2554 v3s16 p = blockpos + v3s16(0,0,0);
2555 addUpdateMeshTask(p, ack_to_server, urgent);
2557 catch(InvalidPositionException &e){}
2559 if(nodepos.X == blockpos_relative.X){
2561 v3s16 p = blockpos + v3s16(-1,0,0);
2562 addUpdateMeshTask(p, false, urgent);
2564 catch(InvalidPositionException &e){}
2566 if(nodepos.Y == blockpos_relative.Y){
2568 v3s16 p = blockpos + v3s16(0,-1,0);
2569 addUpdateMeshTask(p, false, urgent);
2571 catch(InvalidPositionException &e){}
2573 if(nodepos.Z == blockpos_relative.Z){
2575 v3s16 p = blockpos + v3s16(0,0,-1);
2576 addUpdateMeshTask(p, false, urgent);
2578 catch(InvalidPositionException &e){}
2582 ClientEvent Client::getClientEvent()
2584 if(m_client_event_queue.size() == 0)
2587 event.type = CE_NONE;
2590 return m_client_event_queue.pop_front();
2593 void Client::afterContentReceived()
2595 infostream<<"Client::afterContentReceived() started"<<std::endl;
2596 assert(m_itemdef_received);
2597 assert(m_nodedef_received);
2598 assert(texturesReceived());
2600 // remove the information about which checksum each texture
2602 m_media_name_sha1_map.clear();
2604 // Rebuild inherited images and recreate textures
2605 infostream<<"- Rebuilding images and textures"<<std::endl;
2606 m_tsrc->rebuildImagesAndTextures();
2608 // Update texture atlas
2609 infostream<<"- Updating texture atlas"<<std::endl;
2610 if(g_settings->getBool("enable_texture_atlas"))
2611 m_tsrc->buildMainAtlas(this);
2614 m_shsrc->rebuildShaders();
2616 // Update node aliases
2617 infostream<<"- Updating node aliases"<<std::endl;
2618 m_nodedef->updateAliases(m_itemdef);
2620 // Update node textures
2621 infostream<<"- Updating node textures"<<std::endl;
2622 m_nodedef->updateTextures(m_tsrc);
2624 // Preload item textures and meshes if configured to
2625 if(g_settings->getBool("preload_item_visuals"))
2627 verbosestream<<"Updating item textures and meshes"<<std::endl;
2628 std::set<std::string> names = m_itemdef->getAll();
2629 for(std::set<std::string>::const_iterator
2630 i = names.begin(); i != names.end(); ++i){
2631 // Asking for these caches the result
2632 m_itemdef->getInventoryTexture(*i, this);
2633 m_itemdef->getWieldMesh(*i, this);
2637 // Start mesh update thread after setting up content definitions
2638 infostream<<"- Starting mesh update thread"<<std::endl;
2639 m_mesh_update_thread.Start();
2641 infostream<<"Client::afterContentReceived() done"<<std::endl;
2644 float Client::getRTT(void)
2647 return m_con.GetPeerAvgRTT(PEER_ID_SERVER);
2648 } catch(con::PeerNotFoundException &e){
2653 // IGameDef interface
2655 IItemDefManager* Client::getItemDefManager()
2659 INodeDefManager* Client::getNodeDefManager()
2663 ICraftDefManager* Client::getCraftDefManager()
2666 //return m_craftdef;
2668 ITextureSource* Client::getTextureSource()
2672 IShaderSource* Client::getShaderSource()
2676 u16 Client::allocateUnknownNodeId(const std::string &name)
2678 errorstream<<"Client::allocateUnknownNodeId(): "
2679 <<"Client cannot allocate node IDs"<<std::endl;
2681 return CONTENT_IGNORE;
2683 ISoundManager* Client::getSoundManager()
2687 MtEventManager* Client::getEventManager()